| /** |
| * @file set.c |
| * @author Radek Krejci <rkrejci@cesnet.cz> |
| * @brief Generic set routines implementations |
| * |
| * Copyright (c) 2015 - 2018 CESNET, z.s.p.o. |
| * |
| * This source code is licensed under BSD 3-Clause License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://opensource.org/licenses/BSD-3-Clause |
| */ |
| |
| #include "common.h" |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "log.h" |
| #include "set.h" |
| |
| API LY_ERR |
| ly_set_new(struct ly_set **set_p) |
| { |
| LY_CHECK_ARG_RET(NULL, set_p, LY_EINVAL); |
| |
| *set_p = calloc(1, sizeof **set_p); |
| LY_CHECK_ERR_RET(!(*set_p), LOGMEM(NULL), LY_EMEM); |
| |
| return LY_SUCCESS; |
| } |
| |
| API void |
| ly_set_clean(struct ly_set *set, void (*destructor)(void *obj)) |
| { |
| uint32_t u; |
| |
| if (!set) { |
| return; |
| } |
| |
| if (destructor) { |
| for (u = 0; u < set->count; ++u) { |
| destructor(set->objs[u]); |
| } |
| } |
| set->count = 0; |
| } |
| |
| API void |
| ly_set_erase(struct ly_set *set, void (*destructor)(void *obj)) |
| { |
| if (!set) { |
| return; |
| } |
| |
| ly_set_clean(set, destructor); |
| |
| free(set->objs); |
| set->size = 0; |
| set->objs = NULL; |
| } |
| |
| API void |
| ly_set_free(struct ly_set *set, void (*destructor)(void *obj)) |
| { |
| if (!set) { |
| return; |
| } |
| |
| ly_set_erase(set, destructor); |
| |
| free(set); |
| } |
| |
| API ly_bool |
| ly_set_contains(const struct ly_set *set, void *object, uint32_t *index_p) |
| { |
| LY_CHECK_ARG_RET(NULL, set, 0); |
| |
| for (uint32_t i = 0; i < set->count; i++) { |
| if (set->objs[i] == object) { |
| /* object found */ |
| if (index_p) { |
| *index_p = i; |
| } |
| return 1; |
| } |
| } |
| |
| /* object not found */ |
| return 0; |
| } |
| |
| API LY_ERR |
| ly_set_dup(const struct ly_set *set, void *(*duplicator)(void *obj), struct ly_set **newset_p) |
| { |
| struct ly_set *newset; |
| uint32_t u; |
| |
| LY_CHECK_ARG_RET(NULL, set, newset_p, LY_EINVAL); |
| |
| newset = calloc(1, sizeof *newset); |
| LY_CHECK_ERR_RET(!newset, LOGMEM(NULL), LY_EMEM); |
| if (!set->count) { |
| *newset_p = newset; |
| return LY_SUCCESS; |
| } |
| |
| newset->count = set->count; |
| newset->size = set->count; /* optimize the size */ |
| newset->objs = malloc(newset->size * sizeof *(newset->objs)); |
| LY_CHECK_ERR_RET(!newset->objs, LOGMEM(NULL); free(newset), LY_EMEM); |
| if (duplicator) { |
| for (u = 0; u < set->count; ++u) { |
| newset->objs[u] = duplicator(set->objs[u]); |
| } |
| } else { |
| memcpy(newset->objs, set->objs, newset->size * sizeof *(newset->objs)); |
| } |
| |
| *newset_p = newset; |
| return LY_SUCCESS; |
| } |
| |
| API LY_ERR |
| ly_set_add(struct ly_set *set, void *object, ly_bool list, uint32_t *index_p) |
| { |
| void **new; |
| |
| LY_CHECK_ARG_RET(NULL, set, LY_EINVAL); |
| |
| if (!list) { |
| /* search for duplication */ |
| for (uint32_t i = 0; i < set->count; i++) { |
| if (set->objs[i] == object) { |
| /* already in set */ |
| if (index_p) { |
| *index_p = i; |
| } |
| return LY_SUCCESS; |
| } |
| } |
| } |
| |
| if (set->size == set->count) { |
| #define SET_SIZE_STEP 8 |
| new = realloc(set->objs, (set->size + SET_SIZE_STEP) * sizeof *(set->objs)); |
| LY_CHECK_ERR_RET(!new, LOGMEM(NULL), LY_EMEM); |
| set->size += SET_SIZE_STEP; |
| set->objs = new; |
| #undef SET_SIZE_STEP |
| } |
| |
| if (index_p) { |
| *index_p = set->count; |
| } |
| set->objs[set->count++] = object; |
| |
| return LY_SUCCESS; |
| } |
| |
| API LY_ERR |
| ly_set_merge(struct ly_set *trg, const struct ly_set *src, ly_bool list, void *(*duplicator)(void *obj)) |
| { |
| uint32_t u; |
| void *obj; |
| |
| LY_CHECK_ARG_RET(NULL, trg, LY_EINVAL); |
| |
| if (!src) { |
| /* nothing to do */ |
| return LY_SUCCESS; |
| } |
| |
| for (u = 0; u < src->count; ++u) { |
| if (duplicator) { |
| obj = duplicator(src->objs[u]); |
| } else { |
| obj = src->objs[u]; |
| } |
| LY_CHECK_RET(ly_set_add(trg, obj, list, NULL)); |
| } |
| |
| return LY_SUCCESS; |
| } |
| |
| API LY_ERR |
| ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj)) |
| { |
| LY_CHECK_ARG_RET(NULL, set, LY_EINVAL); |
| LY_CHECK_ERR_RET(index >= set->count, LOGARG(NULL, index), LY_EINVAL); |
| |
| if (destructor) { |
| destructor(set->objs[index]); |
| } |
| if (index == set->count - 1) { |
| /* removing last item in set */ |
| set->objs[index] = NULL; |
| } else { |
| /* removing item somewhere in a middle, so put there the last item */ |
| set->objs[index] = set->objs[set->count - 1]; |
| set->objs[set->count - 1] = NULL; |
| } |
| set->count--; |
| |
| return LY_SUCCESS; |
| } |
| |
| API LY_ERR |
| ly_set_rm(struct ly_set *set, void *object, void (*destructor)(void *obj)) |
| { |
| uint32_t i; |
| |
| LY_CHECK_ARG_RET(NULL, set, object, LY_EINVAL); |
| |
| /* get index */ |
| for (i = 0; i < set->count; i++) { |
| if (set->objs[i] == object) { |
| break; |
| } |
| } |
| LY_CHECK_ERR_RET((i == set->count), LOGARG(NULL, object), LY_EINVAL); /* object is not in set */ |
| |
| return ly_set_rm_index(set, i, destructor); |
| } |
| |
| LY_ERR |
| ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj)) |
| { |
| if (destructor) { |
| destructor(set->objs[index]); |
| } |
| set->count--; |
| if (index == set->count) { |
| /* removing last item in set */ |
| set->objs[index] = NULL; |
| } else { |
| /* removing item somewhere in a middle, move following items */ |
| memmove(set->objs + index, set->objs + index + 1, (set->count - index) * sizeof *set->objs); |
| set->objs[set->count] = NULL; |
| } |
| |
| return LY_SUCCESS; |
| } |