/* $OpenBSD: slist.c,v 1.8 2021/03/29 03:54:39 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan 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: * 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 AUTHOR 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 AUTHOR 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 * provide list accesses against any pointer */ /* * void **list; * list_size; // allocated size for the list * last_idx; // The last index * first_idx; // The first index * * - first_idx == last_idx means empty. * - 0 <= (fist_idx and last_idx) <= (list_size - 1) * - Allocated size is (last_idx - first_idx) % list_size. * To make the code for checking empty and full simple, we use only * list_size-1 items instead of using the full size. * - XXX Wnen itr_curr is removed... */ #include #include #include #include #include "slist.h" #define GROW_SIZE 256 #define PTR_SIZE (sizeof(intptr_t)) #ifdef SLIST_DEBUG #include #define SLIST_ASSERT(cond) \ if (!(cond)) { \ fprintf(stderr, \ "\nAssertion failure("#cond") at (%s):%s:%d\n", \ __func__, __FILE__, __LINE__); \ } #else #define SLIST_ASSERT(cond) #endif /** * Returns 1 if a index is in the valid range, otherwise returns 0. */ #define VALID_IDX(_list, _idx) \ (((_list)->first_idx <= (_list)->last_idx) \ ? (((_list)->first_idx <= (_idx) && (_idx) < (_list)->last_idx)? 1 : 0)\ : (((_list)->first_idx <= (_idx) || (_idx) < (_list)->last_idx)? 1 : 0)) /** Convert an index into the internal index */ #define REAL_IDX(_list, _idx) \ (((_list)->first_idx + (_idx)) % (_list)->list_size) /** Convert a virtual index into the index */ #define VIRT_IDX(_list, _idx) (((_list)->first_idx <= (_idx)) \ ? (_idx) - (_list)->first_idx \ : (_list)->list_size - (_list)->first_idx + (_idx)) /** Decrement an index */ #define DECR_IDX(_list, _memb) \ (_list)->_memb = ((_list)->list_size + --((_list)->_memb)) \ % (_list)->list_size /** Increment an index */ #define INCR_IDX(_list, _memb) \ (_list)->_memb = (++((_list)->_memb)) % (_list)->list_size static int slist_grow (slist *); static int slist_grow0 (slist *, int); static __inline void slist_swap0 (slist *, int, int); static __inline void slist_qsort0(slist *, int (*)(const void *, const void *), int, int); #define itr_is_valid(list) ((list)->itr_next >= 0) #define itr_invalidate(list) ((list)->itr_next = -1) /** Initialize a slist */ void slist_init(slist *list) { memset(list, 0, sizeof(slist)); itr_invalidate(list); } /** * Specify the size of a list. The size must be specified with the size you * want to use +1. Extra 1 entry is for internal use. The size doesn't shrink. */ int slist_set_size(slist *list, int size) { if (size > list->list_size) return slist_grow0(list, size - list->list_size); return 0; } /** Finish using. Free the buffers and reinit. */ void slist_fini(slist *list) { free(list->list); slist_init(list); } /** The length of the list */ int slist_length(slist *list) { return (list->first_idx <= list->last_idx) ? (list->last_idx - list->first_idx) : (list->list_size - list->first_idx + list->last_idx); } /** Extend the size. Used if the list is full. */ static int slist_grow0(slist *list, int grow_sz) { int size_new; void **list_new = NULL; /* just return if it is possible to add one item */ if (slist_length(list) + 1 < list->list_size) /* "+ 1" to avoid the situation list_size == slist_length() */ return 0; size_new = list->list_size + grow_sz; if ((list_new = realloc(list->list, PTR_SIZE * size_new)) == NULL) return -1; memset(&list_new[list->list_size], 0, PTR_SIZE * (size_new - list->list_size)); list->list = list_new; if (list->last_idx < list->first_idx && list->last_idx >= 0) { /* * space is created at the right side when center has space, * so move left side to right side */ if (list->last_idx <= grow_sz) { /* * The right side has enough space, so move the left * side to right side. */ memmove(&list->list[list->list_size], &list->list[0], PTR_SIZE * list->last_idx); list->last_idx = list->list_size + list->last_idx; } else { /* * Copy the left side to right side as long as we * can */ memmove(&list->list[list->list_size], &list->list[0], PTR_SIZE * grow_sz); /* Shift the remain to left */ memmove(&list->list[0], &list->list[grow_sz], PTR_SIZE *(list->last_idx - grow_sz)); list->last_idx -= grow_sz; } } list->list_size = size_new; return 0; } static int slist_grow(slist *list) { return slist_grow0(list, GROW_SIZE); } /** Add an item to a list */ void * slist_add(slist *list, void *item) { if (slist_grow(list) != 0) return NULL; list->list[list->last_idx] = item; if (list->itr_next == -2) { /* the iterator points the last, update it. */ list->itr_next = list->last_idx; } INCR_IDX(list, last_idx); return item; } #define slist_get0(list_, idx) ((list_)->list[REAL_IDX((list_), (idx))]) /** Add all items in add_items to a list. */ int slist_add_all(slist *list, slist *add_items) { int i, n; n = slist_length(add_items); for (i = 0; i < n; i++) { if (slist_add(list, slist_get0(add_items, i)) == NULL) return 1; } return 0; } /** Return "idx"th item. */ void * slist_get(slist *list, int idx) { SLIST_ASSERT(idx >= 0); SLIST_ASSERT(slist_length(list) > idx); if (idx < 0 || slist_length(list) <= idx) return NULL; return slist_get0(list, idx); } /** Store a value in "idx"th item */ int slist_set(slist *list, int idx, void *item) { SLIST_ASSERT(idx >= 0); SLIST_ASSERT(slist_length(list) > idx); if (idx < 0 || slist_length(list) <= idx) return -1; list->list[REAL_IDX(list, idx)] = item; return 0; } /** Remove the 1st entry and return it. */ void * slist_remove_first(slist *list) { void *oldVal; if (slist_length(list) <= 0) return NULL; oldVal = list->list[list->first_idx]; if (itr_is_valid(list) && list->itr_next == list->first_idx) INCR_IDX(list, itr_next); if (!VALID_IDX(list, list->itr_next)) itr_invalidate(list); INCR_IDX(list, first_idx); return oldVal; } /** Remove the last entry and return it */ void * slist_remove_last(slist *list) { if (slist_length(list) <= 0) return NULL; DECR_IDX(list, last_idx); if (!VALID_IDX(list, list->itr_next)) itr_invalidate(list); return list->list[list->last_idx]; } /** Remove all entries */ void slist_remove_all(slist *list) { void **list0 = list->list; slist_init(list); list->list = list0; } /* Swap items. This doesn't check boundary. */ static __inline void slist_swap0(slist *list, int m, int n) { void *m0; itr_invalidate(list); /* Invalidate iterator */ m0 = list->list[REAL_IDX(list, m)]; list->list[REAL_IDX(list, m)] = list->list[REAL_IDX(list, n)]; list->list[REAL_IDX(list, n)] = m0; } /** Swap between mth and nth */ void slist_swap(slist *list, int m, int n) { int len; len = slist_length(list); SLIST_ASSERT(m >= 0); SLIST_ASSERT(n >= 0); SLIST_ASSERT(len > m); SLIST_ASSERT(len > n); if (m < 0 || n < 0) return; if (m >= len || n >= len) return; slist_swap0(list, m, n); } /** Remove "idx"th item */ void * slist_remove(slist *list, int idx) { int first, last, idx0, reset_itr; void *oldVal; SLIST_ASSERT(idx >= 0); SLIST_ASSERT(slist_length(list) > idx); if (idx < 0 || slist_length(list) <= idx) return NULL; idx0 = REAL_IDX(list, idx); oldVal = list->list[idx0]; reset_itr = 0; first = -1; last = -1; if (list->itr_next == idx0) { INCR_IDX(list, itr_next); if (!VALID_IDX(list, list->itr_next)) list->itr_next = -2; /* on the last item */ } /* should we reduce the last side or the first side? */ if (list->first_idx < list->last_idx) { /* take the smaller side */ if (idx0 - list->first_idx < list->last_idx - idx0) { first = list->first_idx; INCR_IDX(list, first_idx); } else { last = list->last_idx; DECR_IDX(list, last_idx); } } else { /* * 0 < last (unused) first < idx < size, so let's reduce the * first. */ if (list->first_idx <= idx0) { first = list->first_idx; INCR_IDX(list, first_idx); } else { last = list->last_idx; DECR_IDX(list, last_idx); } } /* the last side */ if (last != -1 && last != 0 && last != idx0) { /* move left the items that is from idx0 to the last */ if (itr_is_valid(list) && idx0 <= list->itr_next && list->itr_next <= last) { DECR_IDX(list, itr_next); if (!VALID_IDX(list, list->itr_next)) itr_invalidate(list); } memmove(&list->list[idx0], &list->list[idx0 + 1], (PTR_SIZE) * (last - idx0)); } /* the first side */ if (first != -1 && first != idx0) { /* move right the items that is from first to the idx0 */ if (itr_is_valid(list) && first <= list->itr_next && list->itr_next <= idx0) { INCR_IDX(list, itr_next); if (!VALID_IDX(list, list->itr_next)) itr_invalidate(list); } memmove(&list->list[first + 1], &list->list[first], (PTR_SIZE) * (idx0 - first)); } if (list->first_idx == list->last_idx) { list->first_idx = 0; list->last_idx = 0; } return oldVal; } /** * Shuffle items. */ void slist_shuffle(slist *list) { int i, len; len = slist_length(list); for (i = len; i > 1; i--) slist_swap0(list, i - 1, (int)arc4random_uniform(i)); } /** Init an iterator. Only one iterator exists. */ void slist_itr_first(slist *list) { list->itr_next = list->first_idx; if (!VALID_IDX(list, list->itr_next)) itr_invalidate(list); } /** * Return whether a iterator can go to the next item. * @return Return 1 if the iterator can return the next item. * Return 0 it reaches the end of the list or the list is modified * destructively. */ int slist_itr_has_next(slist *list) { if (list->itr_next < 0) return 0; return VALID_IDX(list, list->itr_next); } /** Return the next item and iterate to the next */ void * slist_itr_next(slist *list) { void *rval; if (!itr_is_valid(list)) return NULL; SLIST_ASSERT(VALID_IDX(list, list->itr_next)); if (list->list == NULL) return NULL; rval = list->list[list->itr_next]; list->itr_curr = list->itr_next; INCR_IDX(list, itr_next); if (!VALID_IDX(list, list->itr_next)) list->itr_next = -2; /* on the last item */ return rval; } /** Delete the current iterated item */ void * slist_itr_remove(slist *list) { SLIST_ASSERT(list != NULL); return slist_remove(list, VIRT_IDX(list, list->itr_curr)); } /** Sort the list items by quick sort algorithm using given compar */ void slist_qsort(slist *list, int (*compar)(const void *, const void *)) { if (list->first_idx != list->last_idx) /* is not empty */ slist_qsort0(list, compar, 0, slist_length(list) - 1); } static __inline void slist_qsort0(slist *list, int (*compar)(const void *, const void *), int l, int r) { int i, j; void *p; i = l; j = r; p = slist_get0(list, (j + i) / 2); while (i <= j) { while (compar(slist_get0(list, i), p) < 0) i++; while (compar(slist_get0(list, j), p) > 0) j--; if (i <= j) slist_swap0(list, i++, j--); } if (l < j) slist_qsort0(list, compar, l, j); if (i < r) slist_qsort0(list, compar, i, r); }