1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2002-2003, Network Appliance, Inc. All rights reserved.
24  */
25 
26 /*
27  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /*
32  *
33  * MODULE: dapl_llist.c
34  *
35  * PURPOSE: Manage doubly linked lists within the DAPL Reference Implementation
36  *
37  *	A link list head points to the first member of a linked list, but
38  *	is itself not a member of the list.
39  *
40  *          +---------------------------------------------+
41  *          |      entry         entry         entry      |
42  *  HEAD -> |    +-------+     +-------+     +-------+    |
43  *          +--> | flink | --> | flink | --> | flink | >--+
44  *	         | data  |     | data  |     | data  |
45  *	    +--< | blink | <-- | blink | <-- | blink | <--|
46  *          |    +-------+     +-------+     +-------+    |
47  *          |                                             |
48  *          +---------------------------------------------+
49  *
50  * Note:  Each of the remove functions takes an assertion failure if
51  *        an element cannot be removed from the list.
52  *
53  * $Id: dapl_llist.c,v 1.9 2003/06/13 12:21:11 sjs2 Exp $
54  */
55 
56 #include "dapl.h"
57 
58 /*
59  * dapl_llist_init_head()
60  *
61  * Purpose: initialize a linked list head
62  */
63 void
dapl_llist_init_head(DAPL_LLIST_HEAD * head)64 dapl_llist_init_head(DAPL_LLIST_HEAD *head)
65 {
66 	*head = NULL;
67 }
68 
69 /*
70  * dapl_llist_init_entry()
71  *
72  * Purpose: initialize a linked list entry
73  */
74 void
dapl_llist_init_entry(DAPL_LLIST_ENTRY * entry)75 dapl_llist_init_entry(DAPL_LLIST_ENTRY *entry)
76 {
77 	entry->blink = NULL;
78 	entry->flink = NULL;
79 	entry->data = 0;
80 	entry->list_head = NULL;
81 }
82 
83 /*
84  * dapl_llist_is_empty()
85  *
86  * Purpose: returns TRUE if the linked list is empty
87  */
88 DAT_BOOLEAN
dapl_llist_is_empty(DAPL_LLIST_HEAD * head)89 dapl_llist_is_empty(DAPL_LLIST_HEAD *head)
90 {
91 	return (*head == NULL);
92 }
93 
94 /*
95  * dapl_llist_add_head()
96  *
97  * Purpose: Add an entry to the head of a linked list
98  */
99 void
dapl_llist_add_head(DAPL_LLIST_HEAD * head,DAPL_LLIST_ENTRY * entry,void * data)100 dapl_llist_add_head(DAPL_LLIST_HEAD *head,
101 		DAPL_LLIST_ENTRY *entry,
102 		void *data)
103 {
104 	DAPL_LLIST_ENTRY *first;
105 
106 	/* deal with empty list */
107 	if (dapl_llist_is_empty(head)) {
108 		entry->flink = entry;
109 		entry->blink = entry;
110 	} else {
111 		first = *head;
112 		entry->flink = first;
113 		entry->blink = first->blink;
114 		first->blink->flink = entry;
115 		first->blink = entry;
116 	}
117 
118 	*head		= entry;
119 	entry->data	= data;
120 	entry->list_head = head;
121 }
122 
123 /*
124  * dapl_llist_add_tail()
125  *
126  * Purpose: Add an entry to the tail of a linked list
127  */
128 void
dapl_llist_add_tail(DAPL_LLIST_HEAD * head,DAPL_LLIST_ENTRY * entry,void * data)129 dapl_llist_add_tail(DAPL_LLIST_HEAD *head,
130 	DAPL_LLIST_ENTRY *entry,
131 	void *data)
132 {
133 	DAPL_LLIST_ENTRY *last;
134 
135 	/* deal with empty list */
136 	if (dapl_llist_is_empty(head)) {
137 		*head = entry;
138 		entry->flink = entry;
139 		entry->blink = entry;
140 	} else {
141 		last = (*head)->blink;
142 		entry->flink = last->flink;
143 		entry->blink = last;
144 		last->flink->blink = entry;
145 		last->flink = entry;
146 	}
147 	entry->data = data;
148 	entry->list_head = head;
149 }
150 
151 
152 /*
153  * dapl_llist_add_entry()
154  *
155  * Purpose: Add an entry before an element in the list
156  */
157 void
dapl_llist_add_entry(DAPL_LLIST_HEAD * head,DAPL_LLIST_ENTRY * entry,DAPL_LLIST_ENTRY * new_entry,void * data)158 dapl_llist_add_entry(DAPL_LLIST_HEAD * head,
159 		DAPL_LLIST_ENTRY * entry,
160 		DAPL_LLIST_ENTRY * new_entry,
161 		void * data)
162 {
163 	DAPL_LLIST_ENTRY *last;
164 
165 	/* deal with empty list */
166 	if (dapl_llist_is_empty(head)) {
167 		*head = entry;
168 		entry->flink = entry;
169 		entry->blink = entry;
170 	} else {
171 		last = entry->blink;
172 		entry->blink = new_entry;
173 		last->flink  = new_entry;
174 
175 		new_entry->flink = entry;
176 		new_entry->blink = last;
177 
178 	}
179 	new_entry->data = data;
180 	new_entry->list_head = head;
181 }
182 
183 /*
184  * dapl_llist_remove_head()
185  *
186  * Purpose: Remove the first entry of a linked list
187  */
188 void *
dapl_llist_remove_head(DAPL_LLIST_HEAD * head)189 dapl_llist_remove_head(DAPL_LLIST_HEAD *head)
190 {
191 	DAPL_LLIST_ENTRY *first;
192 
193 	dapl_os_assert(!dapl_llist_is_empty(head));
194 	first = *head;
195 	*head = first->flink;
196 
197 	first->flink->blink = first->blink;
198 	first->blink->flink = first->flink;
199 
200 	if (first->flink == first) {
201 		*head = NULL;
202 	}
203 	/* clean up the links for good measure */
204 	first->flink = NULL;
205 	first->blink = NULL;
206 	first->list_head = NULL;
207 	return (first->data);
208 }
209 
210 /*
211  * dapl_llist_remove_tail()
212  *
213  * Purpose: Remove the last entry of a linked list
214  */
215 void *
dapl_llist_remove_tail(DAPL_LLIST_HEAD * head)216 dapl_llist_remove_tail(DAPL_LLIST_HEAD *head)
217 {
218 	DAPL_LLIST_ENTRY *last;
219 
220 	dapl_os_assert(!dapl_llist_is_empty(head));
221 	last = (*head)->blink;
222 
223 	last->blink->flink = last->flink;
224 	last->flink->blink = last->blink;
225 
226 	if (last->flink == last) {
227 		*head = NULL;
228 	}
229 	/* clean up the links for good measure */
230 	last->flink = NULL;
231 	last->blink = NULL;
232 	last->list_head = NULL;
233 
234 	return (last->data);
235 }
236 
237 /*
238  * dapl_llist_remove_entry()
239  *
240  * Purpose: Remove the specified entry from a linked list
241  */
242 void *
dapl_llist_remove_entry(DAPL_LLIST_HEAD * head,DAPL_LLIST_ENTRY * entry)243 dapl_llist_remove_entry(DAPL_LLIST_HEAD *head, DAPL_LLIST_ENTRY *entry)
244 {
245 	DAPL_LLIST_ENTRY *first;
246 
247 	dapl_os_assert(!dapl_llist_is_empty(head));
248 	first = *head;
249 
250 	/* if it's the first entry, pull it off */
251 	if (first == entry) {
252 		(*head) = first->flink;
253 		/* if it was the only entry, kill the list */
254 		if (first->flink == first) {
255 			(*head) = NULL;
256 		}
257 	}
258 #ifdef VERIFY_LINKED_LIST
259 	else {
260 		DAPL_LLIST_ENTRY *try_entry;
261 
262 		try_entry = first->flink;
263 		for (;;) {
264 			if (try_entry == first) {
265 				/*
266 				 * not finding the element on the list
267 				 * is a BAD thing
268 				 */
269 				dapl_os_assert(0);
270 				break;
271 			}
272 			if (try_entry == entry) {
273 				break;
274 			}
275 			try_entry = try_entry->flink;
276 		}
277 	}
278 #endif /* VERIFY_LINKED_LIST */
279 
280 	dapl_os_assert(entry->list_head == head);
281 	entry->list_head = NULL;
282 
283 	entry->flink->blink = entry->blink;
284 	entry->blink->flink = entry->flink;
285 	entry->flink = NULL;
286 	entry->blink = NULL;
287 
288 	return (entry->data);
289 }
290 
291 /*
292  * dapl_llist_peek_head
293  */
294 
295 void *
dapl_llist_peek_head(DAPL_LLIST_HEAD * head)296 dapl_llist_peek_head(DAPL_LLIST_HEAD *head)
297 {
298 	DAPL_LLIST_ENTRY *first;
299 
300 	dapl_os_assert(!dapl_llist_is_empty(head));
301 	first = *head;
302 	return (first->data);
303 }
304 
305 
306 /*
307  * dapl_llist_next_entry
308  *
309  * Obtain the next entry in the list, return NULL when we get to the
310  * head
311  */
312 
313 void *
dapl_llist_next_entry(IN DAPL_LLIST_HEAD * head,IN DAPL_LLIST_ENTRY * cur_ent)314 dapl_llist_next_entry(IN    DAPL_LLIST_HEAD 	*head,
315     IN    DAPL_LLIST_ENTRY 	*cur_ent)
316 {
317 	DAPL_LLIST_ENTRY *next;
318 
319 	dapl_os_assert(!dapl_llist_is_empty(head));
320 	if (cur_ent == NULL) {
321 		next = *head;
322 	} else {
323 		next = cur_ent->flink;
324 		if (next == *head) {
325 			return (NULL);
326 		}
327 	}
328 	return (next->data);
329 }
330 
331 /*
332  * dapl_llist_debug_print_list()
333  *
334  * Purpose: Prints the linked list for debugging
335  */
336 void
dapl_llist_debug_print_list(DAPL_LLIST_HEAD * head)337 dapl_llist_debug_print_list(DAPL_LLIST_HEAD *head)
338 {
339 	DAPL_LLIST_ENTRY * entry;
340 	DAPL_LLIST_ENTRY * first;
341 	first = *head;
342 	if (!first) {
343 		dapl_dbg_log(DAPL_DBG_TYPE_RTN, "EMPTY_LIST\n");
344 		return;
345 	}
346 	dapl_dbg_log(DAPL_DBG_TYPE_RTN, "HEAD %p\n", *head);
347 	dapl_dbg_log(DAPL_DBG_TYPE_RTN, "Entry %p %p %p %p\n",
348 	    first,
349 	    first->flink,
350 	    first->blink,
351 	    first->data);
352 	entry = first->flink;
353 	while (entry != first) {
354 		dapl_dbg_log(DAPL_DBG_TYPE_RTN, "Entry %p %p %p %p\n",
355 		    entry,
356 		    entry->flink,
357 		    entry->blink,
358 		    entry->data);
359 		entry = entry->flink;
360 	}
361 }
362