xref: /openbsd/usr.sbin/npppd/common/slist.c (revision cca36db2)
1 /*-
2  * Copyright (c) 2009 Internet Initiative Japan Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 /**@file
27  * provide list accesses against any pointer
28  */
29 /*
30  *	void **list;
31  *	list_size;	// allocated size for the list
32  *	last_idx;	// The last index
33  *	first_idx;	// The first index
34  *
35  * - first_idx == last_idx means empty.
36  * - 0 <= (fist_idx and last_idx) <= (list_size - 1)
37  * - Allocated size is (last_idx - first_idx) % list_size.
38  *   To make the code for checking empty and full simple, we use only
39  *   list_size-1 items instead of using the full size.
40  * - XXX Wnen itr_curr is removed...
41  */
42 #include <sys/types.h>
43 
44 #include <stdint.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include "slist.h"
49 
50 #define	GROW_SIZE	256
51 #define	PTR_SIZE	(sizeof(intptr_t))
52 
53 #ifdef	SLIST_DEBUG
54 #include <stdio.h>
55 #define	SLIST_ASSERT(cond)			\
56 	if (!(cond)) {							\
57 		fprintf(stderr,						\
58 		    "\nAssertion failure("#cond") at (%s):%s:%d\n",	\
59 		    __func__, __FILE__, __LINE__);			\
60 	}
61 #else
62 #define	SLIST_ASSERT(cond)
63 #endif
64 
65 /**
66  * Returns 1 if a index is in the valid range, otherwise returns 0.
67  */
68 #define	VALID_IDX(_list, _idx)					\
69 	  (((_list)->first_idx <= (_list)->last_idx)			\
70 	? (((_list)->first_idx <= (_idx) && (_idx) < (_list)->last_idx)? 1 : 0)\
71 	: (((_list)->first_idx <= (_idx) || (_idx) < (_list)->last_idx)? 1 : 0))
72 
73 /** Convert an index into the internal index */
74 #define	REAL_IDX(_list, _idx)						\
75 	(((_list)->first_idx + (_idx)) % (_list)->list_size)
76 
77 /** Convert a virtual index into the index */
78 #define	VIRT_IDX(_list, _idx)	(((_list)->first_idx <= (_idx))	\
79 	? (_idx) - (_list)->first_idx				\
80 	: (_list)->list_size - (_list)->first_idx + (_idx))
81 
82 /** Decrement an index */
83 #define	DECR_IDX(_list, _memb)						\
84 	(_list)->_memb = ((_list)->list_size + --((_list)->_memb))	\
85 	    % (_list)->list_size
86 /** Increment an index */
87 #define	INCR_IDX(_list, _memb)						\
88 	(_list)->_memb = (++((_list)->_memb)) % (_list)->list_size
89 
90 static int          slist_grow (slist *);
91 static int          slist_grow0 (slist *, int);
92 static __inline void  slist_swap0 (slist *, int, int);
93 static __inline void  slist_qsort0(slist *, int (*)(const void *, const void *), int, int);
94 
95 #define	itr_is_valid(list)	((list)->itr_next >= 0)
96 #define	itr_invalidate(list)	((list)->itr_next = -1)
97 
98 /** Initialize a slist */
99 void
100 slist_init(slist *list)
101 {
102 	memset(list, 0, sizeof(slist));
103 	itr_invalidate(list);
104 }
105 
106 /**
107  * Specify the size of a list. The size must be specified with the size you
108  * want to use +1. Extra 1 entry is for internal use. The size doesn't shrink.
109  */
110 int
111 slist_set_size(slist *list, int size)
112 {
113 	if (size > list->list_size)
114 		return slist_grow0(list, size - list->list_size);
115 
116 	return 0;
117 }
118 
119 /** Finish using. Free the buffers and reinit. */
120 void
121 slist_fini(slist *list)
122 {
123 	if (list->list != NULL)
124 		free(list->list);
125 	slist_init(list);
126 }
127 
128 /** The length of the list */
129 int
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
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
193 slist_grow(slist *list)
194 {
195 	return slist_grow0(list, GROW_SIZE);
196 }
197 
198 /** Add an item to a list */
199 void *
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
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 *
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
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 *
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 *
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
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 boudary. */
310 static __inline void
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
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 *
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  * slist_shuffle() uses random(3). Call srandom(3) before use it.
431  */
432 void
433 slist_shuffle(slist *list)
434 {
435 	int i, len;
436 
437 	len = slist_length(list);
438 	for (i = len; i > 1; i--)
439 		slist_swap0(list, i - 1, (int)(random() % i));
440 }
441 
442 /** Init an iterator. Only one iterator exists.  */
443 void
444 slist_itr_first(slist *list)
445 {
446 	list->itr_next = list->first_idx;
447 	if (!VALID_IDX(list, list->itr_next))
448 		itr_invalidate(list);
449 }
450 
451 /**
452  * Return whether a iterator can go to the next item.
453  * @return Return 1 if the iterator can return the next item.
454  *	Return 0 it reaches the end of the list or the list is modified
455  *	destructively.
456  */
457 int
458 slist_itr_has_next(slist *list)
459 {
460 	if (list->itr_next < 0)
461 		return 0;
462 	return VALID_IDX(list, list->itr_next);
463 }
464 
465 /** Return the next item and iterate to the next */
466 void *
467 slist_itr_next(slist *list)
468 {
469 	void *rval;
470 
471 	if (!itr_is_valid(list))
472 		return NULL;
473 	SLIST_ASSERT(VALID_IDX(list, list->itr_next));
474 
475 	if (list->list == NULL)
476 		return NULL;
477 
478 	rval = list->list[list->itr_next];
479 	list->itr_curr = list->itr_next;
480 	INCR_IDX(list, itr_next);
481 
482 	if (!VALID_IDX(list, list->itr_next))
483 		list->itr_next = -2;	/* on the last item */
484 
485 	return rval;
486 }
487 
488 /** Delete the current iterated item  */
489 void *
490 slist_itr_remove(slist *list)
491 {
492 	SLIST_ASSERT(list != NULL);
493 
494 	return slist_remove(list, VIRT_IDX(list, list->itr_curr));
495 }
496 
497 /** Sort the list items by quick sort algorithm using given compar */
498 void
499 slist_qsort(slist *list, int (*compar)(const void *, const void *))
500 {
501 	if (list->first_idx != list->last_idx)	/* is not empty */
502 		slist_qsort0(list, compar, 0, slist_length(list) - 1);
503 }
504 
505 static __inline void
506 slist_qsort0(slist *list, int (*compar)(const void *, const void *), int l,
507     int r)
508 {
509 	int i, j;
510 	void *p;
511 
512 	i = l;
513 	j = r;
514 	p = slist_get0(list, (j + i) / 2);
515 	while (i <= j) {
516 		while (compar(slist_get0(list, i), p) < 0)
517 			i++;
518 		while (compar(slist_get0(list, j), p) > 0)
519 			j--;
520 		if (i <= j)
521 			slist_swap0(list, i++, j--);
522 	}
523 	if (l < j)
524 		slist_qsort0(list, compar, l, j);
525 	if (i < r)
526 		slist_qsort0(list, compar, i, r);
527 }
528