• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ptrlist.c
3  *
4  * (C) Copyright Linus Torvalds 2003-2005
5  */
6 
7 ///
8 // Pointer list manipulation
9 // -------------------------
10 //
11 // The data structure handled here is designed to hold pointers
12 // but two special cases need to be avoided or need special care:
13 // * NULL is used by {PREPARE,NEXT}_PTR_LIST() to indicate the end-of-list.
14 //   Thus, NULL can't be stored in lists using this API but is fine to
15 //   use with FOR_EACH_PTR() and its variants.
16 // * VOID is used to replace a removed pseudo 'usage'. Since phi-nodes
17 //   (OP_PHI) use a list to store their operands, a VOID in a phi-node
18 //   list must be ignored since it represents a removed operand. As
19 //   consequence, VOIDs must never be used as phi-node operand.
20 //   This is fine since phi-nodes make no sense with void values
21 //   but VOID is also used for invalid types and in case of errors.
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 
27 #include "ptrlist.h"
28 #include "allocate.h"
29 #include "compat.h"
30 
31 __DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
32 __ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
33 
34 ///
35 // get the size of a ptrlist
36 // @head: the head of the list
37 // @return: the size of the list given by @head.
ptr_list_size(struct ptr_list * head)38 int ptr_list_size(struct ptr_list *head)
39 {
40 	int nr = 0;
41 
42 	if (head) {
43 		struct ptr_list *list = head;
44 		do {
45 			nr += list->nr - list->rm;
46 		} while ((list = list->next) != head);
47 	}
48 	return nr;
49 }
50 
51 ///
52 // test if a list is empty
53 // @head: the head of the list
54 // @return: ``true`` if the list is empty, ``false`` otherwise.
ptr_list_empty(const struct ptr_list * head)55 bool ptr_list_empty(const struct ptr_list *head)
56 {
57 	const struct ptr_list *list = head;
58 
59 	if (!head)
60 		return true;
61 
62 	do {
63 		if (list->nr - list->rm)
64 			return false;
65 	} while ((list = list->next) != head);
66 
67 	return true;
68 }
69 
70 ///
71 // test is a list contains more than one element
72 // @head: the head of the list
73 // @return: ``true`` if the list has more than 1 element, ``false`` otherwise.
ptr_list_multiple(const struct ptr_list * head)74 bool ptr_list_multiple(const struct ptr_list *head)
75 {
76 	const struct ptr_list *list = head;
77 	int nr = 0;
78 
79 	if (!head)
80 		return false;
81 
82 	do {
83 		nr += list->nr - list->rm;
84 		if (nr > 1)
85 			return true;
86 	} while ((list = list->next) != head);
87 
88 	return false;
89 }
90 
91 ///
92 // get the first element of a ptrlist
93 // @head: the head of the list
94 // @return: the first element of the list or ``NULL`` if the list is empty
first_ptr_list(struct ptr_list * head)95 void *first_ptr_list(struct ptr_list *head)
96 {
97 	struct ptr_list *list = head;
98 
99 	if (!head)
100 		return NULL;
101 
102 	while (list->nr == 0) {
103 		list = list->next;
104 		if (list == head)
105 			return NULL;
106 	}
107 	return PTR_ENTRY_NOTAG(list, 0);
108 }
109 
110 ///
111 // get the last element of a ptrlist
112 // @head: the head of the list
113 // @return: the last element of the list or ``NULL`` if the list is empty
last_ptr_list(struct ptr_list * head)114 void *last_ptr_list(struct ptr_list *head)
115 {
116 	struct ptr_list *list;
117 
118 	if (!head)
119 		return NULL;
120 	list = head->prev;
121 	while (list->nr == 0) {
122 		if (list == head)
123 			return NULL;
124 		list = list->prev;
125 	}
126 	return PTR_ENTRY_NOTAG(list, list->nr-1);
127 }
128 
129 ///
130 // get the nth element of a ptrlist
131 // @head: the head of the list
132 // @return: the nth element of the list or ``NULL`` if the list is too short.
ptr_list_nth_entry(struct ptr_list * list,unsigned int idx)133 void *ptr_list_nth_entry(struct ptr_list *list, unsigned int idx)
134 {
135 	struct ptr_list *head = list;
136 
137 	if (!head)
138 		return NULL;
139 
140 	do {
141 		unsigned int nr = list->nr;
142 
143 		if (idx < nr)
144 			return list->list[idx];
145 		else
146 			idx -= nr;
147 	} while ((list = list->next) != head);
148 	return NULL;
149 }
150 
151 ///
152 // linearize the entries of a list
153 //
154 // @head: the list to be linearized
155 // @arr: a ``void*`` array to fill with @head's entries
156 // @max: the maximum number of entries to store into @arr
157 // @return: the number of entries in the list.
158 //
159 // Linearize the entries of a list up to a total of @max,
160 // and return the number of entries in the list.
161 //
162 // The array to linearize into (@arr) should really
163 // be ``void *x[]``, but we want to let people fill in any kind
164 // of pointer array, so let's just call it ``void **``.
linearize_ptr_list(struct ptr_list * head,void ** arr,int max)165 int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
166 {
167 	int nr = 0;
168 	if (head && max > 0) {
169 		struct ptr_list *list = head;
170 
171 		do {
172 			int i = list->nr;
173 			nr += i;
174 			if (max == 0)
175 				continue;
176 			if (i > max)
177 				i = max;
178 			memcpy(arr, list->list, i*sizeof(void *));
179 			arr += i;
180 			max -= i;
181 		} while ((list = list->next) != head);
182 	}
183 	return nr;
184 }
185 
186 ///
187 // pack a ptrlist
188 //
189 // @listp: a pointer to the list to be packed.
190 //
191 // When we've walked the list and deleted entries,
192 // we may need to re-pack it so that we don't have
193 // any empty blocks left (empty blocks upset the
194 // walking code).
pack_ptr_list(struct ptr_list ** listp)195 void pack_ptr_list(struct ptr_list **listp)
196 {
197 	struct ptr_list *head = *listp;
198 
199 	if (head) {
200 		struct ptr_list *entry = head;
201 		do {
202 			struct ptr_list *next;
203 restart:
204 			next = entry->next;
205 			if (!entry->nr) {
206 				struct ptr_list *prev;
207 				if (next == entry) {
208 					__free_ptrlist(entry);
209 					*listp = NULL;
210 					return;
211 				}
212 				prev = entry->prev;
213 				prev->next = next;
214 				next->prev = prev;
215 				__free_ptrlist(entry);
216 				if (entry == head) {
217 					*listp = next;
218 					head = next;
219 					entry = next;
220 					goto restart;
221 				}
222 			}
223 			entry = next;
224 		} while (entry != head);
225 	}
226 }
227 
228 ///
229 // split a ptrlist block
230 // @head: the ptrlist block to be split
231 //
232 // A new block is inserted just after @head and the entries
233 // at the half end of @head are moved to this new block.
234 // The goal being to create space inside @head for a new entry.
split_ptr_list_head(struct ptr_list * head)235 void split_ptr_list_head(struct ptr_list *head)
236 {
237 	int old = head->nr, nr = old / 2;
238 	struct ptr_list *newlist = __alloc_ptrlist(0);
239 	struct ptr_list *next = head->next;
240 
241 	old -= nr;
242 	head->nr = old;
243 	newlist->next = next;
244 	next->prev = newlist;
245 	newlist->prev = head;
246 	head->next = newlist;
247 	newlist->nr = nr;
248 	memcpy(newlist->list, head->list + old, nr * sizeof(void *));
249 	memset(head->list + old, 0xf0, nr * sizeof(void *));
250 }
251 
252 ///
253 // add an entry to a ptrlist
254 // @listp: a pointer to the list
255 // @ptr: the entry to add to the list
256 // @return: the address where the new entry is stored.
257 //
258 // :note: code must not use this function and should use
259 //	:func:`add_ptr_list` instead.
__add_ptr_list(struct ptr_list ** listp,void * ptr)260 void **__add_ptr_list(struct ptr_list **listp, void *ptr)
261 {
262 	struct ptr_list *list = *listp;
263 	struct ptr_list *last = NULL; /* gcc complains needlessly */
264 	void **ret;
265 	int nr;
266 
267 	if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
268 		struct ptr_list *newlist = __alloc_ptrlist(0);
269 		if (!list) {
270 			newlist->next = newlist;
271 			newlist->prev = newlist;
272 			*listp = newlist;
273 		} else {
274 			newlist->prev = last;
275 			newlist->next = list;
276 			list->prev = newlist;
277 			last->next = newlist;
278 		}
279 		last = newlist;
280 		nr = 0;
281 	}
282 	ret = last->list + nr;
283 	*ret = ptr;
284 	nr++;
285 	last->nr = nr;
286 	return ret;
287 }
288 
289 ///
290 // add a tagged entry to a ptrlist
291 // @listp: a pointer to the list
292 // @ptr: the entry to add to the list
293 // @tag: the tag to add to @ptr
294 // @return: the address where the new entry is stored.
295 //
296 // :note: code must not use this function and should use
297 //	:func:`add_ptr_list_tag` instead.
__add_ptr_list_tag(struct ptr_list ** listp,void * ptr,unsigned long tag)298 void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag)
299 {
300 	/* The low two bits are reserved for tags */
301 	assert((3 & (unsigned long)ptr) == 0);
302 	assert((~3 & tag) == 0);
303 
304 	ptr = (void *)(tag | (unsigned long)ptr);
305 
306 	return __add_ptr_list(listp, ptr);
307 }
308 
309 ///
310 // test if some entry is already present in a ptrlist
311 // @list: the head of the list
312 // @entry: the entry to test
313 // @return: ``true`` if the entry is already present, ``false`` otherwise.
lookup_ptr_list_entry(const struct ptr_list * head,const void * entry)314 bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry)
315 {
316 	const struct ptr_list *list = head;
317 
318 	if (!head)
319 		return false;
320 	do {
321 		int nr = list->nr;
322 		int i;
323 		for (i = 0; i < nr; i++)
324 			if (list->list[i] == entry)
325 				return true;
326 	} while ((list = list->next) != head);
327 	return false;
328 }
329 
330 ///
331 // delete an entry from a ptrlist
332 // @list: a pointer to the list
333 // @entry: the item to be deleted
334 // @count: the minimum number of times @entry should be deleted or 0.
delete_ptr_list_entry(struct ptr_list ** list,void * entry,int count)335 int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
336 {
337 	void *ptr;
338 
339 	FOR_EACH_PTR(*list, ptr) {
340 		if (ptr == entry) {
341 			DELETE_CURRENT_PTR(ptr);
342 			if (!--count)
343 				goto out;
344 		}
345 	} END_FOR_EACH_PTR(ptr);
346 	assert(count <= 0);
347 out:
348 	pack_ptr_list(list);
349 	return count;
350 }
351 
352 ///
353 // replace an entry in a ptrlist
354 // @list: a pointer to the list
355 // @old_ptr: the entry to be replaced
356 // @new_ptr: the new entry
357 // @count: the minimum number of times @entry should be deleted or 0.
replace_ptr_list_entry(struct ptr_list ** list,void * old_ptr,void * new_ptr,int count)358 int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr,
359 	void *new_ptr, int count)
360 {
361 	void *ptr;
362 
363 	FOR_EACH_PTR(*list, ptr) {
364 		if (ptr==old_ptr) {
365 			REPLACE_CURRENT_PTR(ptr, new_ptr);
366 			if (!--count)
367 				goto out;
368 		}
369 	}END_FOR_EACH_PTR(ptr);
370 	assert(count <= 0);
371 out:
372 	return count;
373 }
374 
375 ///
376 // remove the last entry of a ptrlist
377 // @head: a pointer to the list
378 // @return: the last element of the list or NULL if the list is empty.
379 //
380 // :note: this doesn't repack the list
undo_ptr_list_last(struct ptr_list ** head)381 void * undo_ptr_list_last(struct ptr_list **head)
382 {
383 	struct ptr_list *last, *first = *head;
384 
385 	if (!first)
386 		return NULL;
387 	last = first;
388 	do {
389 		last = last->prev;
390 		if (last->nr) {
391 			void *ptr;
392 			int nr = --last->nr;
393 			ptr = last->list[nr];
394 			last->list[nr] = (void *)0xf1f1f1f1;
395 			return ptr;
396 		}
397 	} while (last != first);
398 	return NULL;
399 }
400 
401 ///
402 // remove the last entry and repack the list
403 // @head: a pointer to the list
404 // @return: the last element of the list or NULL if the list is empty.
delete_ptr_list_last(struct ptr_list ** head)405 void * delete_ptr_list_last(struct ptr_list **head)
406 {
407 	void *ptr = NULL;
408 	struct ptr_list *last, *first = *head;
409 
410 	if (!first)
411 		return NULL;
412 	last = first->prev;
413 	if (last->nr)
414 		ptr = last->list[--last->nr];
415 	if (last->nr <=0) {
416 		first->prev = last->prev;
417 		last->prev->next = first;
418 		if (last == first)
419 			*head = NULL;
420 		__free_ptrlist(last);
421 	}
422 	return ptr;
423 }
424 
425 ///
426 // concat two ptrlists
427 // @a: the source list
428 // @b: a pointer to the destination list.
429 // The element of @a are added at the end of @b.
concat_ptr_list(struct ptr_list * a,struct ptr_list ** b)430 void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
431 {
432 	void *entry;
433 	FOR_EACH_PTR(a, entry) {
434 		add_ptr_list(b, entry);
435 	} END_FOR_EACH_PTR(entry);
436 }
437 
438 ///
439 // copy the elements of a list at the end of another list.
440 // @listp: a pointer to the destination list.
441 // @src: the head of the source list.
copy_ptr_list(struct ptr_list ** listp,struct ptr_list * src)442 void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src)
443 {
444 	struct ptr_list *head, *tail;
445 	struct ptr_list *cur = src;
446 	int idx;
447 
448 	if (!src)
449 		return;
450 	head = *listp;
451 	if (!head) {
452 		*listp = src;
453 		return;
454 	}
455 
456 	tail = head->prev;
457 	idx = tail->nr;
458 	do {
459 		struct ptr_list *next;
460 		int nr = cur->nr;
461 		int i;
462 		for (i = 0; i < nr;) {
463 			void *ptr = cur->list[i++];
464 			if (!ptr)
465 				continue;
466 			if (idx >= LIST_NODE_NR) {
467 				struct ptr_list *prev = tail;
468 				tail = __alloc_ptrlist(0);
469 				prev->next = tail;
470 				tail->prev = prev;
471 				prev->nr = idx;
472 				idx = 0;
473 			}
474 			tail->list[idx++] = ptr;
475 		}
476 
477 		next = cur->next;
478 		__free_ptrlist(cur);
479 		cur = next;
480 	} while (cur != src);
481 
482 	tail->nr = idx;
483 	head->prev = tail;
484 	tail->next = head;
485 }
486 
487 ///
488 // free a ptrlist
489 // @listp: a pointer to the list
490 // Each blocks of the list are freed (but the entries
491 // themselves are not freed).
492 //
493 // :note: code must not use this function and should use
494 //	the macro :func:`free_ptr_list` instead.
__free_ptr_list(struct ptr_list ** listp)495 void __free_ptr_list(struct ptr_list **listp)
496 {
497 	struct ptr_list *tmp, *list = *listp;
498 
499 	if (!list)
500 		return;
501 
502 	list->prev->next = NULL;
503 	while (list) {
504 		tmp = list;
505 		list = list->next;
506 		__free_ptrlist(tmp);
507 	}
508 
509 	*listp = NULL;
510 }
511