xref: /openbsd-src/usr.sbin/npppd/common/slist.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
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 
94 #define	itr_is_valid(list)	((list)->itr_next >= 0)
95 #define	itr_invalidate(list)	((list)->itr_next = -1)
96 
97 /** Initialize a slist */
98 void
99 slist_init(slist *list)
100 {
101 	memset(list, 0, sizeof(slist));
102 	itr_invalidate(list);
103 }
104 
105 /**
106  * Specify the size of a list. The size must be specified with the size you
107  * want to use +1. Extra 1 entry is for internal use. The size doesn't shrink.
108  */
109 int
110 slist_set_size(slist *list, int size)
111 {
112 	if (size > list->list_size)
113 		return slist_grow0(list, size - list->list_size);
114 
115 	return 0;
116 }
117 
118 /** Finish using. Free the buffers and reinit. */
119 void
120 slist_fini(slist *list)
121 {
122 	if (list->list != NULL)
123 		free(list->list);
124 	slist_init(list);
125 }
126 
127 /** The length of the list */
128 int
129 slist_length(slist *list)
130 {
131 	return
132 	      (list->first_idx <= list->last_idx)
133 	    ? (list->last_idx - list->first_idx)
134 	    : (list->list_size - list->first_idx + list->last_idx);
135 }
136 
137 /** Extend the size. Used if the list is full. */
138 static int
139 slist_grow0(slist *list, int grow_sz)
140 {
141 	int size_new;
142 	void **list_new = NULL;
143 
144  	/* just return if it is possible to add one item */
145 	if (slist_length(list) + 1 < list->list_size)
146 		/* "+ 1" to avoid the situation list_size == slist_length() */
147 		return 0;
148 
149 	size_new = list->list_size + grow_sz;
150 	if ((list_new = realloc(list->list, PTR_SIZE * size_new))
151 	    == NULL)
152 		return -1;
153 
154 	memset(&list_new[list->list_size], 0,
155 	    PTR_SIZE * (size_new - list->list_size));
156 
157 	list->list = list_new;
158 	if (list->last_idx < list->first_idx && list->last_idx >= 0) {
159 
160 		/*
161 		 * space is created at the right side when center has space,
162 		 * so move left side to right side
163 		 */
164 		if (list->last_idx <= grow_sz) {
165 			/*
166 			 * The right side has enough space, so move the left
167 			 * side to right side.
168 			 */
169 			memmove(&list->list[list->list_size],
170 			    &list->list[0], PTR_SIZE * list->last_idx);
171 			list->last_idx = list->list_size + list->last_idx;
172 		} else {
173 			/*
174 			 * Copy the left side to right side as long as we
175 			 * can
176 			 */
177 			memmove(&list->list[list->list_size],
178 			    &list->list[0], PTR_SIZE * grow_sz);
179 			/* Shift the remain to left */
180 			memmove(&list->list[0], &list->list[grow_sz],
181 			    PTR_SIZE *(list->last_idx - grow_sz));
182 
183 			list->last_idx -= grow_sz;
184 		}
185 	}
186 	list->list_size = size_new;
187 
188 	return 0;
189 }
190 
191 static int
192 slist_grow(slist *list)
193 {
194 	return slist_grow0(list, GROW_SIZE);
195 }
196 
197 /** Add an item to a list */
198 void *
199 slist_add(slist *list, void *item)
200 {
201 	if (slist_grow(list) != 0)
202 		return NULL;
203 
204 	list->list[list->last_idx] = item;
205 
206 	if (list->itr_next == -2) {
207 		/* the iterator points the last, update it. */
208 		list->itr_next = list->last_idx;
209 	}
210 
211 	INCR_IDX(list, last_idx);
212 
213 	return item;
214 }
215 
216 #define slist_get0(list_, idx)	((list_)->list[REAL_IDX((list_), (idx))])
217 
218 /** Add all items in add_items to a list. */
219 int
220 slist_add_all(slist *list, slist *add_items)
221 {
222 	int i, n;
223 
224 	n = slist_length(add_items);
225 	for (i = 0; i < n; i++) {
226 		if (slist_add(list, slist_get0(add_items, i)) ==  NULL)
227 			return 1;
228 	}
229 
230 	return 0;
231 }
232 
233 /** Return "idx"th item. */
234 void *
235 slist_get(slist *list, int idx)
236 {
237 	SLIST_ASSERT(idx >= 0);
238 	SLIST_ASSERT(slist_length(list) > idx);
239 
240 	if (idx < 0 || slist_length(list) <= idx)
241 		return NULL;
242 
243 	return slist_get0(list, idx);
244 }
245 
246 /** Store a value in "idx"th item */
247 int
248 slist_set(slist *list, int idx, void *item)
249 {
250 	SLIST_ASSERT(idx >= 0);
251 	SLIST_ASSERT(slist_length(list) > idx);
252 
253 	if (idx < 0 || slist_length(list) <= idx)
254 		return -1;
255 
256 	list->list[REAL_IDX(list, idx)] = item;
257 
258 	return 0;
259 }
260 
261 /** Remove the 1st entry and return it. */
262 void *
263 slist_remove_first(slist *list)
264 {
265 	void *oldVal;
266 
267 	if (slist_length(list) <= 0)
268 		return NULL;
269 
270 	oldVal = list->list[list->first_idx];
271 
272 	if (itr_is_valid(list) && list->itr_next == list->first_idx)
273 		INCR_IDX(list, itr_next);
274 
275 	if (!VALID_IDX(list, list->itr_next))
276 		itr_invalidate(list);
277 
278 	INCR_IDX(list, first_idx);
279 
280 	return oldVal;
281 }
282 
283 /** Remove the last entry and return it */
284 void *
285 slist_remove_last(slist *list)
286 {
287 	if (slist_length(list) <= 0)
288 		return NULL;
289 
290 	DECR_IDX(list, last_idx);
291 	if (!VALID_IDX(list, list->itr_next))
292 		itr_invalidate(list);
293 
294 	return list->list[list->last_idx];
295 }
296 
297 /** Remove all entries */
298 void
299 slist_remove_all(slist *list)
300 {
301 	void **list0 = list->list;
302 
303 	slist_init(list);
304 
305 	list->list = list0;
306 }
307 
308 /* Swap items. This doesn't check boudary. */
309 static __inline void
310 slist_swap0(slist *list, int m, int n)
311 {
312 	void *m0;
313 
314 	itr_invalidate(list);	/* Invalidate iterator */
315 
316 	m0 = list->list[REAL_IDX(list, m)];
317 	list->list[REAL_IDX(list, m)] = list->list[REAL_IDX(list, n)];
318 	list->list[REAL_IDX(list, n)] = m0;
319 }
320 
321 /** Swap between mth and nth */
322 void
323 slist_swap(slist *list, int m, int n)
324 {
325 	int len;
326 
327 	len = slist_length(list);
328 	SLIST_ASSERT(m >= 0);
329 	SLIST_ASSERT(n >= 0);
330 	SLIST_ASSERT(len > m);
331 	SLIST_ASSERT(len > n);
332 
333 	if (m < 0 || n < 0)
334 		return;
335 	if (m >= len || n >= len)
336 		return;
337 
338 	slist_swap0(list, m, n);
339 }
340 
341 /** Remove "idx"th item */
342 void *
343 slist_remove(slist *list, int idx)
344 {
345 	int first, last, idx0, reset_itr;
346 	void *oldVal;
347 
348 	SLIST_ASSERT(idx >= 0);
349 	SLIST_ASSERT(slist_length(list) > idx);
350 
351 	if (idx < 0 || slist_length(list) <= idx)
352 		return NULL;
353 
354 	idx0 = REAL_IDX(list, idx);
355 	oldVal = list->list[idx0];
356 	reset_itr = 0;
357 
358 	first = -1;
359 	last = -1;
360 
361 	if (list->itr_next == idx0) {
362 		INCR_IDX(list, itr_next);
363 		if (!VALID_IDX(list, list->itr_next))
364 			list->itr_next = -2;	/* on the last item */
365 	}
366 
367 	/* should we reduce the last side or the first side? */
368 	if (list->first_idx < list->last_idx) {
369 		/* take the smaller side */
370 		if (idx0 - list->first_idx < list->last_idx - idx0) {
371 			first = list->first_idx;
372 			INCR_IDX(list, first_idx);
373 		} else {
374 			last = list->last_idx;
375 			DECR_IDX(list, last_idx);
376 		}
377 	} else {
378 		/*
379 		 * 0 < last (unused) first < idx < size, so let's reduce the
380 		 * first.
381 		 */
382 		if (list->first_idx <= idx0) {
383 			first = list->first_idx;
384 			INCR_IDX(list, first_idx);
385 		} else {
386 			last = list->last_idx;
387 			DECR_IDX(list, last_idx);
388 		}
389 	}
390 
391 	/* the last side */
392 	if (last != -1 && last != 0 && last != idx0) {
393 
394 		/* move left the items that is from idx0 to the last */
395 		if (itr_is_valid(list) &&
396 		    idx0 <= list->itr_next && list->itr_next <= last) {
397 			DECR_IDX(list, itr_next);
398 			if (!VALID_IDX(list, list->itr_next))
399 				itr_invalidate(list);
400 		}
401 
402 		memmove(&list->list[idx0], &list->list[idx0 + 1],
403 		    (PTR_SIZE) * (last - idx0));
404 	}
405 	/* the first side */
406 	if (first != -1 && first != idx0) {
407 
408 		/* move right the items that is from first to the idx0 */
409 		if (itr_is_valid(list) &&
410 		    first <= list->itr_next && list->itr_next <= idx0) {
411 			INCR_IDX(list, itr_next);
412 			if (!VALID_IDX(list, list->itr_next))
413 				itr_invalidate(list);
414 		}
415 
416 		memmove(&list->list[first + 1], &list->list[first],
417 		    (PTR_SIZE) * (idx0 - first));
418 	}
419 	if (list->first_idx == list->last_idx) {
420 		list->first_idx = 0;
421 		list->last_idx = 0;
422 	}
423 
424 	return oldVal;
425 }
426 
427 /**
428  * Shuffle items.
429  * slist_shuffle() uses random(3). Call srandom(3) before use it.
430  */
431 void
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)(random() % i));
439 }
440 
441 /** Init an iterator. Only one iterator exists.  */
442 void
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
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 *
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 *
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