OLD | NEW |
| (Empty) |
1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
4 | |
5 #ifdef DEBUG | |
6 static const char CVS_ID[] = "@(#) $RCSfile: list.c,v $ $Revision: 1.21 $ $Date:
2012/04/25 14:49:26 $"; | |
7 #endif /* DEBUG */ | |
8 | |
9 /* | |
10 * list.c | |
11 * | |
12 * This contains the implementation of NSS's thread-safe linked list. | |
13 */ | |
14 | |
15 #ifndef BASE_H | |
16 #include "base.h" | |
17 #endif /* BASE_H */ | |
18 | |
19 struct nssListElementStr { | |
20 PRCList link; | |
21 void *data; | |
22 }; | |
23 | |
24 typedef struct nssListElementStr nssListElement; | |
25 | |
26 struct nssListStr { | |
27 NSSArena *arena; | |
28 PZLock *lock; | |
29 nssListElement *head; | |
30 PRUint32 count; | |
31 nssListCompareFunc compareFunc; | |
32 nssListSortFunc sortFunc; | |
33 PRBool i_alloced_arena; | |
34 }; | |
35 | |
36 struct nssListIteratorStr { | |
37 PZLock *lock; | |
38 nssList *list; | |
39 nssListElement *current; | |
40 }; | |
41 | |
42 #define NSSLIST_LOCK_IF(list) \ | |
43 if ((list)->lock) PZ_Lock((list)->lock) | |
44 | |
45 #define NSSLIST_UNLOCK_IF(list) \ | |
46 if ((list)->lock) PZ_Unlock((list)->lock) | |
47 | |
48 static PRBool | |
49 pointer_compare(void *a, void *b) | |
50 { | |
51 return (PRBool)(a == b); | |
52 } | |
53 | |
54 static nssListElement * | |
55 nsslist_get_matching_element(nssList *list, void *data) | |
56 { | |
57 PRCList *link; | |
58 nssListElement *node; | |
59 node = list->head; | |
60 if (!node) { | |
61 return NULL; | |
62 } | |
63 link = &node->link; | |
64 while (node) { | |
65 /* using a callback slows things down when it's just compare ... */ | |
66 if (list->compareFunc(node->data, data)) { | |
67 break; | |
68 } | |
69 link = &node->link; | |
70 if (link == PR_LIST_TAIL(&list->head->link)) { | |
71 node = NULL; | |
72 break; | |
73 } | |
74 node = (nssListElement *)PR_NEXT_LINK(&node->link); | |
75 } | |
76 return node; | |
77 } | |
78 | |
79 NSS_IMPLEMENT nssList * | |
80 nssList_Create | |
81 ( | |
82 NSSArena *arenaOpt, | |
83 PRBool threadSafe | |
84 ) | |
85 { | |
86 NSSArena *arena; | |
87 nssList *list; | |
88 PRBool i_alloced; | |
89 if (arenaOpt) { | |
90 arena = arenaOpt; | |
91 i_alloced = PR_FALSE; | |
92 } else { | |
93 arena = nssArena_Create(); | |
94 i_alloced = PR_TRUE; | |
95 } | |
96 if (!arena) { | |
97 return (nssList *)NULL; | |
98 } | |
99 list = nss_ZNEW(arena, nssList); | |
100 if (!list) { | |
101 if (!arenaOpt) { | |
102 NSSArena_Destroy(arena); | |
103 } | |
104 return (nssList *)NULL; | |
105 } | |
106 if (threadSafe) { | |
107 list->lock = PZ_NewLock(nssILockOther); | |
108 if (!list->lock) { | |
109 if (arenaOpt) { | |
110 nss_ZFreeIf(list); | |
111 } else { | |
112 NSSArena_Destroy(arena); | |
113 } | |
114 return (nssList *)NULL; | |
115 } | |
116 } | |
117 list->arena = arena; | |
118 list->i_alloced_arena = i_alloced; | |
119 list->compareFunc = pointer_compare; | |
120 return list; | |
121 } | |
122 | |
123 NSS_IMPLEMENT PRStatus | |
124 nssList_Destroy(nssList *list) | |
125 { | |
126 if (!list->i_alloced_arena) { | |
127 nssList_Clear(list, NULL); | |
128 } | |
129 if (list->lock) { | |
130 (void)PZ_DestroyLock(list->lock); | |
131 } | |
132 if (list->i_alloced_arena) { | |
133 NSSArena_Destroy(list->arena); | |
134 list = NULL; | |
135 } | |
136 nss_ZFreeIf(list); | |
137 return PR_SUCCESS; | |
138 } | |
139 | |
140 NSS_IMPLEMENT void | |
141 nssList_SetCompareFunction(nssList *list, nssListCompareFunc compareFunc) | |
142 { | |
143 list->compareFunc = compareFunc; | |
144 } | |
145 | |
146 NSS_IMPLEMENT void | |
147 nssList_SetSortFunction(nssList *list, nssListSortFunc sortFunc) | |
148 { | |
149 /* XXX if list already has elements, sort them */ | |
150 list->sortFunc = sortFunc; | |
151 } | |
152 | |
153 NSS_IMPLEMENT nssListCompareFunc | |
154 nssList_GetCompareFunction(nssList *list) | |
155 { | |
156 return list->compareFunc; | |
157 } | |
158 | |
159 NSS_IMPLEMENT void | |
160 nssList_Clear(nssList *list, nssListElementDestructorFunc destructor) | |
161 { | |
162 PRCList *link; | |
163 nssListElement *node, *tmp; | |
164 NSSLIST_LOCK_IF(list); | |
165 node = list->head; | |
166 list->head = NULL; | |
167 while (node && list->count > 0) { | |
168 if (destructor) (*destructor)(node->data); | |
169 link = &node->link; | |
170 tmp = (nssListElement *)PR_NEXT_LINK(link); | |
171 PR_REMOVE_LINK(link); | |
172 nss_ZFreeIf(node); | |
173 node = tmp; | |
174 --list->count; | |
175 } | |
176 NSSLIST_UNLOCK_IF(list); | |
177 } | |
178 | |
179 static PRStatus | |
180 nsslist_add_element(nssList *list, void *data) | |
181 { | |
182 nssListElement *node = nss_ZNEW(list->arena, nssListElement); | |
183 if (!node) { | |
184 return PR_FAILURE; | |
185 } | |
186 PR_INIT_CLIST(&node->link); | |
187 node->data = data; | |
188 if (list->head) { | |
189 if (list->sortFunc) { | |
190 PRCList *link; | |
191 nssListElement *currNode; | |
192 currNode = list->head; | |
193 /* insert in ordered list */ | |
194 while (currNode) { | |
195 link = &currNode->link; | |
196 if (list->sortFunc(data, currNode->data) <= 0) { | |
197 /* new element goes before current node */ | |
198 PR_INSERT_BEFORE(&node->link, link); | |
199 /* reset head if this is first */ | |
200 if (currNode == list->head) list->head = node; | |
201 break; | |
202 } | |
203 if (link == PR_LIST_TAIL(&list->head->link)) { | |
204 /* reached end of list, append */ | |
205 PR_INSERT_AFTER(&node->link, link); | |
206 break; | |
207 } | |
208 currNode = (nssListElement *)PR_NEXT_LINK(&currNode->link); | |
209 } | |
210 } else { | |
211 /* not sorting */ | |
212 PR_APPEND_LINK(&node->link, &list->head->link); | |
213 } | |
214 } else { | |
215 list->head = node; | |
216 } | |
217 ++list->count; | |
218 return PR_SUCCESS; | |
219 } | |
220 | |
221 NSS_IMPLEMENT PRStatus | |
222 nssList_Add(nssList *list, void *data) | |
223 { | |
224 PRStatus nssrv; | |
225 NSSLIST_LOCK_IF(list); | |
226 nssrv = nsslist_add_element(list, data); | |
227 NSSLIST_UNLOCK_IF(list); | |
228 return PR_SUCCESS; | |
229 } | |
230 | |
231 NSS_IMPLEMENT PRStatus | |
232 nssList_AddUnique(nssList *list, void *data) | |
233 { | |
234 PRStatus nssrv; | |
235 nssListElement *node; | |
236 NSSLIST_LOCK_IF(list); | |
237 node = nsslist_get_matching_element(list, data); | |
238 if (node) { | |
239 /* already in, finish */ | |
240 NSSLIST_UNLOCK_IF(list); | |
241 return PR_SUCCESS; | |
242 } | |
243 nssrv = nsslist_add_element(list, data); | |
244 NSSLIST_UNLOCK_IF(list); | |
245 return nssrv; | |
246 } | |
247 | |
248 NSS_IMPLEMENT PRStatus | |
249 nssList_Remove(nssList *list, void *data) | |
250 { | |
251 nssListElement *node; | |
252 NSSLIST_LOCK_IF(list); | |
253 node = nsslist_get_matching_element(list, data); | |
254 if (node) { | |
255 if (node == list->head) { | |
256 list->head = (nssListElement *)PR_NEXT_LINK(&node->link); | |
257 } | |
258 PR_REMOVE_LINK(&node->link); | |
259 nss_ZFreeIf(node); | |
260 if (--list->count == 0) { | |
261 list->head = NULL; | |
262 } | |
263 } | |
264 NSSLIST_UNLOCK_IF(list); | |
265 return PR_SUCCESS; | |
266 } | |
267 | |
268 NSS_IMPLEMENT void * | |
269 nssList_Get(nssList *list, void *data) | |
270 { | |
271 nssListElement *node; | |
272 NSSLIST_LOCK_IF(list); | |
273 node = nsslist_get_matching_element(list, data); | |
274 NSSLIST_UNLOCK_IF(list); | |
275 return (node) ? node->data : NULL; | |
276 } | |
277 | |
278 NSS_IMPLEMENT PRUint32 | |
279 nssList_Count(nssList *list) | |
280 { | |
281 return list->count; | |
282 } | |
283 | |
284 NSS_IMPLEMENT PRStatus | |
285 nssList_GetArray(nssList *list, void **rvArray, PRUint32 maxElements) | |
286 { | |
287 nssListElement *node; | |
288 PRUint32 i = 0; | |
289 PR_ASSERT(maxElements > 0); | |
290 node = list->head; | |
291 if (!node) { | |
292 return PR_SUCCESS; | |
293 } | |
294 NSSLIST_LOCK_IF(list); | |
295 while (node) { | |
296 rvArray[i++] = node->data; | |
297 if (i == maxElements) break; | |
298 node = (nssListElement *)PR_NEXT_LINK(&node->link); | |
299 if (node == list->head) { | |
300 break; | |
301 } | |
302 } | |
303 NSSLIST_UNLOCK_IF(list); | |
304 return PR_SUCCESS; | |
305 } | |
306 | |
307 NSS_IMPLEMENT nssList * | |
308 nssList_Clone(nssList *list) | |
309 { | |
310 nssList *rvList; | |
311 nssListElement *node; | |
312 rvList = nssList_Create(NULL, (list->lock != NULL)); | |
313 if (!rvList) { | |
314 return NULL; | |
315 } | |
316 NSSLIST_LOCK_IF(list); | |
317 if (list->count > 0) { | |
318 node = list->head; | |
319 while (PR_TRUE) { | |
320 nssList_Add(rvList, node->data); | |
321 node = (nssListElement *)PR_NEXT_LINK(&node->link); | |
322 if (node == list->head) { | |
323 break; | |
324 } | |
325 } | |
326 } | |
327 NSSLIST_UNLOCK_IF(list); | |
328 return rvList; | |
329 } | |
330 | |
331 NSS_IMPLEMENT nssListIterator * | |
332 nssList_CreateIterator(nssList *list) | |
333 { | |
334 nssListIterator *rvIterator; | |
335 rvIterator = nss_ZNEW(NULL, nssListIterator); | |
336 if (!rvIterator) { | |
337 return NULL; | |
338 } | |
339 rvIterator->list = nssList_Clone(list); | |
340 if (!rvIterator->list) { | |
341 nss_ZFreeIf(rvIterator); | |
342 return NULL; | |
343 } | |
344 rvIterator->current = rvIterator->list->head; | |
345 if (list->lock) { | |
346 rvIterator->lock = PZ_NewLock(nssILockOther); | |
347 if (!rvIterator->lock) { | |
348 nssList_Destroy(rvIterator->list); | |
349 nss_ZFreeIf(rvIterator); | |
350 rvIterator = NULL; | |
351 } | |
352 } | |
353 return rvIterator; | |
354 } | |
355 | |
356 NSS_IMPLEMENT void | |
357 nssListIterator_Destroy(nssListIterator *iter) | |
358 { | |
359 if (iter->lock) { | |
360 (void)PZ_DestroyLock(iter->lock); | |
361 } | |
362 nssList_Destroy(iter->list); | |
363 nss_ZFreeIf(iter); | |
364 } | |
365 | |
366 NSS_IMPLEMENT void * | |
367 nssListIterator_Start(nssListIterator *iter) | |
368 { | |
369 NSSLIST_LOCK_IF(iter); | |
370 if (iter->list->count == 0) { | |
371 return NULL; | |
372 } | |
373 iter->current = iter->list->head; | |
374 return iter->current->data; | |
375 } | |
376 | |
377 NSS_IMPLEMENT void * | |
378 nssListIterator_Next(nssListIterator *iter) | |
379 { | |
380 nssListElement *node; | |
381 PRCList *link; | |
382 if (iter->list->count == 1 || iter->current == NULL) { | |
383 /* Reached the end of the list. Don't change the state, force to | |
384 * user to call nssList_Finish to clean up. | |
385 */ | |
386 return NULL; | |
387 } | |
388 node = (nssListElement *)PR_NEXT_LINK(&iter->current->link); | |
389 link = &node->link; | |
390 if (link == PR_LIST_TAIL(&iter->list->head->link)) { | |
391 /* Signal the end of the list. */ | |
392 iter->current = NULL; | |
393 return node->data; | |
394 } | |
395 iter->current = node; | |
396 return node->data; | |
397 } | |
398 | |
399 NSS_IMPLEMENT PRStatus | |
400 nssListIterator_Finish(nssListIterator *iter) | |
401 { | |
402 iter->current = iter->list->head; | |
403 return (iter->lock) ? PZ_Unlock(iter->lock) : PR_SUCCESS; | |
404 } | |
405 | |
OLD | NEW |