src/kv_list.c

Tue, 26 Aug 2025 21:55:19 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 26 Aug 2025 21:55:19 +0200
changeset 1350
189756516eaa
parent 1348
a1da355ed3b8
permissions
-rw-r--r--

implement kv-list to a point where it correctly behaves like a list

that means no lookup-map aspects are implemented just yet

relates to #461

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2025 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.
 */

#include "cx/kv_list.h"
#include "cx/hash_map.h"
#include "cx/linked_list.h"

typedef struct cx_kv_list_s cx_kv_list;

struct cx_kv_list_map_s {
    struct cx_hash_map_s map_base;
    /** Back-reference to the list. */
    cx_kv_list *list;
};

/** The list aspect (must have the same layout as the normal linked list). */
struct cx_kv_list_list_s {
    struct cx_list_s list_base;
    void *begin;
    void *end;
};

struct cx_kv_list_s {
    struct cx_kv_list_list_s list;
    /** The lookup map - stores pointers to the nodes. */
    struct cx_kv_list_map_s *map;
    const cx_list_class *list_methods;
    const cx_map_class *map_methods;
};

static void cx_kvl_deallocate(struct cx_list_s *list) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    // free the map first
    kv_list->map_methods->deallocate(&kv_list->map->map_base.base);
    kv_list->list_methods->deallocate(list);
}

static void *cx_kvl_insert_element(
        struct cx_list_s *list,
        size_t index,
        const void *data
) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    // TODO: trick the base method by adding the required space for the key to the elem_size
    return kv_list->list_methods->insert_element(list, index, data);
}

static size_t cx_kvl_insert_array(
        struct cx_list_s *list,
        size_t index,
        const void *data,
        size_t n
) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    // TODO: trick the base method by adding the required space for the key to the elem_size
    return kv_list->list_methods->insert_array(list, index, data, n);
}

static size_t cx_kvl_insert_sorted(
        struct cx_list_s *list,
        const void *sorted_data,
        size_t n
) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    // TODO: trick the base method by adding the required space for the key to the elem_size
    return kv_list->list_methods->insert_sorted(list, sorted_data, n);
}

static int cx_kvl_insert_iter(
        struct cx_iterator_s *iter,
        const void *elem,
        int prepend
) {
    cx_kv_list *kv_list = (cx_kv_list*)iter->src_handle.m;
    // TODO: trick the base method by adding the required space for the key to the elem_size
    return kv_list->list_methods->insert_iter(iter, elem, prepend);
}

static size_t cx_kvl_remove(
        struct cx_list_s *list,
        size_t index,
        size_t num,
        void *targetbuf
) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    // TODO: always use the target buffer to get the element first,
    //       then obtain the key, remove it from the map,
    //       and finally call any destructors manually
    return kv_list->list_methods->remove(list, index, num, targetbuf);
}

static void cx_kvl_clear(struct cx_list_s *list) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    kv_list->list_methods->clear(list);
    // also clear all lookup entries
    cxMapClear(&kv_list->map->map_base.base);
}

static int cx_kvl_swap(
        struct cx_list_s *list,
        size_t i,
        size_t j
) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    return kv_list->list_methods->swap(list, i, j);
}

static void *cx_kvl_at(
        const struct cx_list_s *list,
        size_t index
) {
    const cx_kv_list *kv_list = (const cx_kv_list*)list;
    return kv_list->list_methods->at(list, index);
}

static size_t cx_kvl_find_remove(
        struct cx_list_s *list,
        const void *elem,
        bool remove
) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    // TODO: implement removal of the key in the map
    return kv_list->list_methods->find_remove(list, elem, remove);
}

static void cx_kvl_sort(struct cx_list_s *list) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    kv_list->list_methods->sort(list);
}

static int cx_kvl_compare(
        const struct cx_list_s *list,
        const struct cx_list_s *other
) {
    // TODO: make it so that comparison with normal linked lists is also optimized
    const cx_kv_list *kv_list = (const cx_kv_list*)list;
    return kv_list->list_methods->compare(list, other);
}

static void cx_kvl_reverse(struct cx_list_s *list) {
    cx_kv_list *kv_list = (cx_kv_list*)list;
    kv_list->list_methods->reverse(list);
}

static struct cx_iterator_s cx_kvl_iterator(
        const struct cx_list_s *list,
        size_t index,
        bool backward
) {
    const cx_kv_list *kv_list = (const cx_kv_list*)list;
    // TODO: cannot really forward, because mutating iterators must be able to remove the element
    return kv_list->list_methods->iterator(list, index, backward);
}

static cx_list_class cx_kv_list_class = {
    cx_kvl_deallocate,
    cx_kvl_insert_element,
    cx_kvl_insert_array,
    cx_kvl_insert_sorted,
    cx_kvl_insert_iter,
    cx_kvl_remove,
    cx_kvl_clear,
    cx_kvl_swap,
    cx_kvl_at,
    cx_kvl_find_remove,
    cx_kvl_sort,
    cx_kvl_compare,
    cx_kvl_reverse,
    cx_kvl_iterator,
};

CxList *cxKvListCreate(
        const CxAllocator *allocator,
        cx_compare_func comparator,
        size_t elem_size
) {
    if (allocator == NULL) {
        allocator = cxDefaultAllocator;
    }

    // create a normal linked list and a normal hash map, first
    CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
    if (list == NULL) return NULL; // LCOV_EXCL_LINE
    CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
    if (map == NULL) { // LCOV_EXCL_START
        cxListFree(list);
        return NULL;
    } // LCOV_EXCL_STOP

    // reallocate the map to add memory for the list back-reference
    struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map, sizeof(struct cx_kv_list_map_s));
    
    // reallocate the list to add memory for storing the metadata
    cx_kv_list *kv_list = cxRealloc(allocator, list, sizeof(cx_kv_list));

    // if any of the reallocations failed, we bail out
    if (kv_map != NULL && kv_list != NULL) {
        map = (CxMap*) kv_map;
        list = (CxList*) kv_list;
    } else { // LCOV_EXCL_START
        cxListFree(list);
        cxMapFree(map);
        return NULL;
    } // LCOV_EXCL_STOP

    // combine the list and the map aspect
    kv_list->map = kv_map;
    kv_map->list = kv_list;

    // remember the base methods and override them
    kv_list->list_methods = list->cl;
    kv_list->map_methods = map->cl;
    list->cl = &cx_kv_list_class;
    // TODO: override all map members
    //       and remember to set the sorted flag of the list to false in the put/emplace methods!

    return list;
}

CxMap *cxKvListCreateAsMap(
        const CxAllocator *allocator,
        cx_compare_func comparator,
        size_t elem_size
) {
    CxList *list = cxKvListCreate(allocator, comparator, elem_size);
    return list == NULL ? NULL : cxKvListAsMap(list);
}

CxList *cxKvListAsList(CxMap *map) {
    return &((struct cx_kv_list_map_s*)map)->list->list.list_base;
}

CxMap *cxKvListAsMap(CxList *list) {
    return &((cx_kv_list*)list)->map->map_base.base;
}

int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key) {
    return -1;
}

int cx_kv_list_remove_key(CxList *list, size_t index, CxHashKey key) {
    return -1;
}

int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value) {
    return -1;
}

mercurial