xref: /illumos-gate/usr/src/tools/smatch/src/ptrlist.c (revision 1f5207b7)
1 /*
2  * ptrlist.c
3  *
4  * Pointer list manipulation
5  *
6  * (C) Copyright Linus Torvalds 2003-2005
7  */
8 #include <stdlib.h>
9 #include <string.h>
10 #include <assert.h>
11 
12 #include "ptrlist.h"
13 #include "allocate.h"
14 #include "compat.h"
15 
16 __DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
17 __ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
18 __ALLOCATOR(struct ptr_list, "rl ptr list", rl_ptrlist);
19 
20 int ptr_list_size(struct ptr_list *head)
21 {
22 	int nr = 0;
23 
24 	if (head) {
25 		struct ptr_list *list = head;
26 		do {
27 			nr += list->nr - list->rm;
28 		} while ((list = list->next) != head);
29 	}
30 	return nr;
31 }
32 
33 /*
34  * Linearize the entries of a list up to a total of 'max',
35  * and return the nr of entries linearized.
36  *
37  * The array to linearize into (second argument) should really
38  * be "void *x[]", but we want to let people fill in any kind
39  * of pointer array, so let's just call it "void **".
40  */
41 int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
42 {
43 	int nr = 0;
44 	if (head && max > 0) {
45 		struct ptr_list *list = head;
46 
47 		do {
48 			int i = list->nr;
49 			if (i > max)
50 				i = max;
51 			memcpy(arr, list->list, i*sizeof(void *));
52 			arr += i;
53 			nr += i;
54 			max -= i;
55 			if (!max)
56 				break;
57 		} while ((list = list->next) != head);
58 	}
59 	return nr;
60 }
61 
62 /*
63  * When we've walked the list and deleted entries,
64  * we may need to re-pack it so that we don't have
65  * any empty blocks left (empty blocks upset the
66  * walking code
67  */
68 void pack_ptr_list(struct ptr_list **listp)
69 {
70 	struct ptr_list *head = *listp;
71 
72 	if (head) {
73 		struct ptr_list *entry = head;
74 		do {
75 			struct ptr_list *next;
76 restart:
77 			next = entry->next;
78 			if (!entry->nr) {
79 				struct ptr_list *prev;
80 				if (next == entry) {
81 					__free_ptrlist(entry);
82 					*listp = NULL;
83 					return;
84 				}
85 				prev = entry->prev;
86 				prev->next = next;
87 				next->prev = prev;
88 				__free_ptrlist(entry);
89 				if (entry == head) {
90 					*listp = next;
91 					head = next;
92 					entry = next;
93 					goto restart;
94 				}
95 			}
96 			entry = next;
97 		} while (entry != head);
98 	}
99 }
100 
101 void split_ptr_list_head(struct ptr_list *head)
102 {
103 	int old = head->nr, nr = old / 2;
104 	struct ptr_list *newlist = __alloc_ptrlist(0);
105 	struct ptr_list *next = head->next;
106 
107 	old -= nr;
108 	head->nr = old;
109 	newlist->next = next;
110 	next->prev = newlist;
111 	newlist->prev = head;
112 	head->next = newlist;
113 	newlist->nr = nr;
114 	memcpy(newlist->list, head->list + old, nr * sizeof(void *));
115 	memset(head->list + old, 0xf0, nr * sizeof(void *));
116 }
117 
118 int rl_ptrlist_hack;
119 void **__add_ptr_list(struct ptr_list **listp, void *ptr, unsigned long tag)
120 {
121 	struct ptr_list *list = *listp;
122 	struct ptr_list *last = NULL; /* gcc complains needlessly */
123 	void **ret;
124 	int nr;
125 
126 	/* The low two bits are reserved for tags */
127 	assert((3 & (unsigned long)ptr) == 0);
128 	assert((~3 & tag) == 0);
129 	ptr = (void *)(tag | (unsigned long)ptr);
130 
131 	if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
132 		struct ptr_list *newlist;
133 
134 		if (rl_ptrlist_hack)
135 			newlist = __alloc_rl_ptrlist(0);
136 		else
137 			newlist = __alloc_ptrlist(0);
138 		if (!list) {
139 			newlist->next = newlist;
140 			newlist->prev = newlist;
141 			*listp = newlist;
142 		} else {
143 			newlist->prev = last;
144 			newlist->next = list;
145 			list->prev = newlist;
146 			last->next = newlist;
147 		}
148 		last = newlist;
149 		nr = 0;
150 	}
151 	ret = last->list + nr;
152 	*ret = ptr;
153 	nr++;
154 	last->nr = nr;
155 	return ret;
156 }
157 
158 int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
159 {
160 	void *ptr;
161 
162 	FOR_EACH_PTR(*list, ptr) {
163 		if (ptr == entry) {
164 			DELETE_CURRENT_PTR(ptr);
165 			if (!--count)
166 				goto out;
167 		}
168 	} END_FOR_EACH_PTR(ptr);
169 	assert(count <= 0);
170 out:
171 	pack_ptr_list(list);
172 	return count;
173 }
174 
175 int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr, void *new_ptr, int count)
176 {
177 	void *ptr;
178 
179 	FOR_EACH_PTR(*list, ptr) {
180 		if (ptr==old_ptr) {
181 			REPLACE_CURRENT_PTR(ptr, new_ptr);
182 			if (!--count)
183 				goto out;
184 		}
185 	}END_FOR_EACH_PTR(ptr);
186 	assert(count <= 0);
187 out:
188 	return count;
189 }
190 
191 /* This removes the last entry, but doesn't pack the ptr list */
192 void * undo_ptr_list_last(struct ptr_list **head)
193 {
194 	struct ptr_list *last, *first = *head;
195 
196 	if (!first)
197 		return NULL;
198 	last = first;
199 	do {
200 		last = last->prev;
201 		if (last->nr) {
202 			void *ptr;
203 			int nr = --last->nr;
204 			ptr = last->list[nr];
205 			last->list[nr] = (void *)0xf1f1f1f1;
206 			return ptr;
207 		}
208 	} while (last != first);
209 	return NULL;
210 }
211 
212 void * delete_ptr_list_last(struct ptr_list **head)
213 {
214 	void *ptr = NULL;
215 	struct ptr_list *last, *first = *head;
216 
217 	if (!first)
218 		return NULL;
219 	last = first->prev;
220 	if (last->nr)
221 		ptr = last->list[--last->nr];
222 	if (last->nr <=0) {
223 		first->prev = last->prev;
224 		last->prev->next = first;
225 		if (last == first)
226 			*head = NULL;
227 		__free_ptrlist(last);
228 	}
229 	return ptr;
230 }
231 
232 void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
233 {
234 	void *entry;
235 	FOR_EACH_PTR(a, entry) {
236 		add_ptr_list(b, entry);
237 	} END_FOR_EACH_PTR(entry);
238 }
239 
240 void __free_ptr_list(struct ptr_list **listp)
241 {
242 	struct ptr_list *tmp, *list = *listp;
243 
244 	if (!list)
245 		return;
246 
247 	list->prev->next = NULL;
248 	while (list) {
249 		tmp = list;
250 		list = list->next;
251 		__free_ptrlist(tmp);
252 	}
253 
254 	*listp = NULL;
255 }
256