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