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