1 /* $OpenBSD: slist.c,v 1.8 2021/03/29 03:54:39 yasuoka Exp $ */
2 /*-
3 * Copyright (c) 2009 Internet Initiative Japan Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27 /**@file
28 * provide list accesses against any pointer
29 */
30 /*
31 * void **list;
32 * list_size; // allocated size for the list
33 * last_idx; // The last index
34 * first_idx; // The first index
35 *
36 * - first_idx == last_idx means empty.
37 * - 0 <= (fist_idx and last_idx) <= (list_size - 1)
38 * - Allocated size is (last_idx - first_idx) % list_size.
39 * To make the code for checking empty and full simple, we use only
40 * list_size-1 items instead of using the full size.
41 * - XXX Wnen itr_curr is removed...
42 */
43 #include <sys/types.h>
44
45 #include <stdint.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include "slist.h"
50
51 #define GROW_SIZE 256
52 #define PTR_SIZE (sizeof(intptr_t))
53
54 #ifdef SLIST_DEBUG
55 #include <stdio.h>
56 #define SLIST_ASSERT(cond) \
57 if (!(cond)) { \
58 fprintf(stderr, \
59 "\nAssertion failure("#cond") at (%s):%s:%d\n", \
60 __func__, __FILE__, __LINE__); \
61 }
62 #else
63 #define SLIST_ASSERT(cond)
64 #endif
65
66 /**
67 * Returns 1 if a index is in the valid range, otherwise returns 0.
68 */
69 #define VALID_IDX(_list, _idx) \
70 (((_list)->first_idx <= (_list)->last_idx) \
71 ? (((_list)->first_idx <= (_idx) && (_idx) < (_list)->last_idx)? 1 : 0)\
72 : (((_list)->first_idx <= (_idx) || (_idx) < (_list)->last_idx)? 1 : 0))
73
74 /** Convert an index into the internal index */
75 #define REAL_IDX(_list, _idx) \
76 (((_list)->first_idx + (_idx)) % (_list)->list_size)
77
78 /** Convert a virtual index into the index */
79 #define VIRT_IDX(_list, _idx) (((_list)->first_idx <= (_idx)) \
80 ? (_idx) - (_list)->first_idx \
81 : (_list)->list_size - (_list)->first_idx + (_idx))
82
83 /** Decrement an index */
84 #define DECR_IDX(_list, _memb) \
85 (_list)->_memb = ((_list)->list_size + --((_list)->_memb)) \
86 % (_list)->list_size
87 /** Increment an index */
88 #define INCR_IDX(_list, _memb) \
89 (_list)->_memb = (++((_list)->_memb)) % (_list)->list_size
90
91 static int slist_grow (slist *);
92 static int slist_grow0 (slist *, int);
93 static __inline void slist_swap0 (slist *, int, int);
94 static __inline void slist_qsort0(slist *, int (*)(const void *, const void *), int, int);
95
96 #define itr_is_valid(list) ((list)->itr_next >= 0)
97 #define itr_invalidate(list) ((list)->itr_next = -1)
98
99 /** Initialize a slist */
100 void
slist_init(slist * list)101 slist_init(slist *list)
102 {
103 memset(list, 0, sizeof(slist));
104 itr_invalidate(list);
105 }
106
107 /**
108 * Specify the size of a list. The size must be specified with the size you
109 * want to use +1. Extra 1 entry is for internal use. The size doesn't shrink.
110 */
111 int
slist_set_size(slist * list,int size)112 slist_set_size(slist *list, int size)
113 {
114 if (size > list->list_size)
115 return slist_grow0(list, size - list->list_size);
116
117 return 0;
118 }
119
120 /** Finish using. Free the buffers and reinit. */
121 void
slist_fini(slist * list)122 slist_fini(slist *list)
123 {
124 free(list->list);
125 slist_init(list);
126 }
127
128 /** The length of the list */
129 int
slist_length(slist * list)130 slist_length(slist *list)
131 {
132 return
133 (list->first_idx <= list->last_idx)
134 ? (list->last_idx - list->first_idx)
135 : (list->list_size - list->first_idx + list->last_idx);
136 }
137
138 /** Extend the size. Used if the list is full. */
139 static int
slist_grow0(slist * list,int grow_sz)140 slist_grow0(slist *list, int grow_sz)
141 {
142 int size_new;
143 void **list_new = NULL;
144
145 /* just return if it is possible to add one item */
146 if (slist_length(list) + 1 < list->list_size)
147 /* "+ 1" to avoid the situation list_size == slist_length() */
148 return 0;
149
150 size_new = list->list_size + grow_sz;
151 if ((list_new = realloc(list->list, PTR_SIZE * size_new))
152 == NULL)
153 return -1;
154
155 memset(&list_new[list->list_size], 0,
156 PTR_SIZE * (size_new - list->list_size));
157
158 list->list = list_new;
159 if (list->last_idx < list->first_idx && list->last_idx >= 0) {
160
161 /*
162 * space is created at the right side when center has space,
163 * so move left side to right side
164 */
165 if (list->last_idx <= grow_sz) {
166 /*
167 * The right side has enough space, so move the left
168 * side to right side.
169 */
170 memmove(&list->list[list->list_size],
171 &list->list[0], PTR_SIZE * list->last_idx);
172 list->last_idx = list->list_size + list->last_idx;
173 } else {
174 /*
175 * Copy the left side to right side as long as we
176 * can
177 */
178 memmove(&list->list[list->list_size],
179 &list->list[0], PTR_SIZE * grow_sz);
180 /* Shift the remain to left */
181 memmove(&list->list[0], &list->list[grow_sz],
182 PTR_SIZE *(list->last_idx - grow_sz));
183
184 list->last_idx -= grow_sz;
185 }
186 }
187 list->list_size = size_new;
188
189 return 0;
190 }
191
192 static int
slist_grow(slist * list)193 slist_grow(slist *list)
194 {
195 return slist_grow0(list, GROW_SIZE);
196 }
197
198 /** Add an item to a list */
199 void *
slist_add(slist * list,void * item)200 slist_add(slist *list, void *item)
201 {
202 if (slist_grow(list) != 0)
203 return NULL;
204
205 list->list[list->last_idx] = item;
206
207 if (list->itr_next == -2) {
208 /* the iterator points the last, update it. */
209 list->itr_next = list->last_idx;
210 }
211
212 INCR_IDX(list, last_idx);
213
214 return item;
215 }
216
217 #define slist_get0(list_, idx) ((list_)->list[REAL_IDX((list_), (idx))])
218
219 /** Add all items in add_items to a list. */
220 int
slist_add_all(slist * list,slist * add_items)221 slist_add_all(slist *list, slist *add_items)
222 {
223 int i, n;
224
225 n = slist_length(add_items);
226 for (i = 0; i < n; i++) {
227 if (slist_add(list, slist_get0(add_items, i)) == NULL)
228 return 1;
229 }
230
231 return 0;
232 }
233
234 /** Return "idx"th item. */
235 void *
slist_get(slist * list,int idx)236 slist_get(slist *list, int idx)
237 {
238 SLIST_ASSERT(idx >= 0);
239 SLIST_ASSERT(slist_length(list) > idx);
240
241 if (idx < 0 || slist_length(list) <= idx)
242 return NULL;
243
244 return slist_get0(list, idx);
245 }
246
247 /** Store a value in "idx"th item */
248 int
slist_set(slist * list,int idx,void * item)249 slist_set(slist *list, int idx, void *item)
250 {
251 SLIST_ASSERT(idx >= 0);
252 SLIST_ASSERT(slist_length(list) > idx);
253
254 if (idx < 0 || slist_length(list) <= idx)
255 return -1;
256
257 list->list[REAL_IDX(list, idx)] = item;
258
259 return 0;
260 }
261
262 /** Remove the 1st entry and return it. */
263 void *
slist_remove_first(slist * list)264 slist_remove_first(slist *list)
265 {
266 void *oldVal;
267
268 if (slist_length(list) <= 0)
269 return NULL;
270
271 oldVal = list->list[list->first_idx];
272
273 if (itr_is_valid(list) && list->itr_next == list->first_idx)
274 INCR_IDX(list, itr_next);
275
276 if (!VALID_IDX(list, list->itr_next))
277 itr_invalidate(list);
278
279 INCR_IDX(list, first_idx);
280
281 return oldVal;
282 }
283
284 /** Remove the last entry and return it */
285 void *
slist_remove_last(slist * list)286 slist_remove_last(slist *list)
287 {
288 if (slist_length(list) <= 0)
289 return NULL;
290
291 DECR_IDX(list, last_idx);
292 if (!VALID_IDX(list, list->itr_next))
293 itr_invalidate(list);
294
295 return list->list[list->last_idx];
296 }
297
298 /** Remove all entries */
299 void
slist_remove_all(slist * list)300 slist_remove_all(slist *list)
301 {
302 void **list0 = list->list;
303
304 slist_init(list);
305
306 list->list = list0;
307 }
308
309 /* Swap items. This doesn't check boundary. */
310 static __inline void
slist_swap0(slist * list,int m,int n)311 slist_swap0(slist *list, int m, int n)
312 {
313 void *m0;
314
315 itr_invalidate(list); /* Invalidate iterator */
316
317 m0 = list->list[REAL_IDX(list, m)];
318 list->list[REAL_IDX(list, m)] = list->list[REAL_IDX(list, n)];
319 list->list[REAL_IDX(list, n)] = m0;
320 }
321
322 /** Swap between mth and nth */
323 void
slist_swap(slist * list,int m,int n)324 slist_swap(slist *list, int m, int n)
325 {
326 int len;
327
328 len = slist_length(list);
329 SLIST_ASSERT(m >= 0);
330 SLIST_ASSERT(n >= 0);
331 SLIST_ASSERT(len > m);
332 SLIST_ASSERT(len > n);
333
334 if (m < 0 || n < 0)
335 return;
336 if (m >= len || n >= len)
337 return;
338
339 slist_swap0(list, m, n);
340 }
341
342 /** Remove "idx"th item */
343 void *
slist_remove(slist * list,int idx)344 slist_remove(slist *list, int idx)
345 {
346 int first, last, idx0, reset_itr;
347 void *oldVal;
348
349 SLIST_ASSERT(idx >= 0);
350 SLIST_ASSERT(slist_length(list) > idx);
351
352 if (idx < 0 || slist_length(list) <= idx)
353 return NULL;
354
355 idx0 = REAL_IDX(list, idx);
356 oldVal = list->list[idx0];
357 reset_itr = 0;
358
359 first = -1;
360 last = -1;
361
362 if (list->itr_next == idx0) {
363 INCR_IDX(list, itr_next);
364 if (!VALID_IDX(list, list->itr_next))
365 list->itr_next = -2; /* on the last item */
366 }
367
368 /* should we reduce the last side or the first side? */
369 if (list->first_idx < list->last_idx) {
370 /* take the smaller side */
371 if (idx0 - list->first_idx < list->last_idx - idx0) {
372 first = list->first_idx;
373 INCR_IDX(list, first_idx);
374 } else {
375 last = list->last_idx;
376 DECR_IDX(list, last_idx);
377 }
378 } else {
379 /*
380 * 0 < last (unused) first < idx < size, so let's reduce the
381 * first.
382 */
383 if (list->first_idx <= idx0) {
384 first = list->first_idx;
385 INCR_IDX(list, first_idx);
386 } else {
387 last = list->last_idx;
388 DECR_IDX(list, last_idx);
389 }
390 }
391
392 /* the last side */
393 if (last != -1 && last != 0 && last != idx0) {
394
395 /* move left the items that is from idx0 to the last */
396 if (itr_is_valid(list) &&
397 idx0 <= list->itr_next && list->itr_next <= last) {
398 DECR_IDX(list, itr_next);
399 if (!VALID_IDX(list, list->itr_next))
400 itr_invalidate(list);
401 }
402
403 memmove(&list->list[idx0], &list->list[idx0 + 1],
404 (PTR_SIZE) * (last - idx0));
405 }
406 /* the first side */
407 if (first != -1 && first != idx0) {
408
409 /* move right the items that is from first to the idx0 */
410 if (itr_is_valid(list) &&
411 first <= list->itr_next && list->itr_next <= idx0) {
412 INCR_IDX(list, itr_next);
413 if (!VALID_IDX(list, list->itr_next))
414 itr_invalidate(list);
415 }
416
417 memmove(&list->list[first + 1], &list->list[first],
418 (PTR_SIZE) * (idx0 - first));
419 }
420 if (list->first_idx == list->last_idx) {
421 list->first_idx = 0;
422 list->last_idx = 0;
423 }
424
425 return oldVal;
426 }
427
428 /**
429 * Shuffle items.
430 */
431 void
slist_shuffle(slist * list)432 slist_shuffle(slist *list)
433 {
434 int i, len;
435
436 len = slist_length(list);
437 for (i = len; i > 1; i--)
438 slist_swap0(list, i - 1, (int)arc4random_uniform(i));
439 }
440
441 /** Init an iterator. Only one iterator exists. */
442 void
slist_itr_first(slist * list)443 slist_itr_first(slist *list)
444 {
445 list->itr_next = list->first_idx;
446 if (!VALID_IDX(list, list->itr_next))
447 itr_invalidate(list);
448 }
449
450 /**
451 * Return whether a iterator can go to the next item.
452 * @return Return 1 if the iterator can return the next item.
453 * Return 0 it reaches the end of the list or the list is modified
454 * destructively.
455 */
456 int
slist_itr_has_next(slist * list)457 slist_itr_has_next(slist *list)
458 {
459 if (list->itr_next < 0)
460 return 0;
461 return VALID_IDX(list, list->itr_next);
462 }
463
464 /** Return the next item and iterate to the next */
465 void *
slist_itr_next(slist * list)466 slist_itr_next(slist *list)
467 {
468 void *rval;
469
470 if (!itr_is_valid(list))
471 return NULL;
472 SLIST_ASSERT(VALID_IDX(list, list->itr_next));
473
474 if (list->list == NULL)
475 return NULL;
476
477 rval = list->list[list->itr_next];
478 list->itr_curr = list->itr_next;
479 INCR_IDX(list, itr_next);
480
481 if (!VALID_IDX(list, list->itr_next))
482 list->itr_next = -2; /* on the last item */
483
484 return rval;
485 }
486
487 /** Delete the current iterated item */
488 void *
slist_itr_remove(slist * list)489 slist_itr_remove(slist *list)
490 {
491 SLIST_ASSERT(list != NULL);
492
493 return slist_remove(list, VIRT_IDX(list, list->itr_curr));
494 }
495
496 /** Sort the list items by quick sort algorithm using given compar */
497 void
slist_qsort(slist * list,int (* compar)(const void *,const void *))498 slist_qsort(slist *list, int (*compar)(const void *, const void *))
499 {
500 if (list->first_idx != list->last_idx) /* is not empty */
501 slist_qsort0(list, compar, 0, slist_length(list) - 1);
502 }
503
504 static __inline void
slist_qsort0(slist * list,int (* compar)(const void *,const void *),int l,int r)505 slist_qsort0(slist *list, int (*compar)(const void *, const void *), int l,
506 int r)
507 {
508 int i, j;
509 void *p;
510
511 i = l;
512 j = r;
513 p = slist_get0(list, (j + i) / 2);
514 while (i <= j) {
515 while (compar(slist_get0(list, i), p) < 0)
516 i++;
517 while (compar(slist_get0(list, j), p) > 0)
518 j--;
519 if (i <= j)
520 slist_swap0(list, i++, j--);
521 }
522 if (l < j)
523 slist_qsort0(list, compar, l, j);
524 if (i < r)
525 slist_qsort0(list, compar, i, r);
526 }
527