xref: /openbsd/usr.sbin/npppd/common/slist.c (revision f5c2ff87)
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