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/. */
6
static const char CVS_ID[] = "@(#) $RCSfile: list.c,v $ $Revision: 1.21 $ $Date: 2012/04/25 14:49:26 $";
12
* This contains the implementation of NSS's thread-safe linked list.
19
struct nssListElementStr {
24
typedef struct nssListElementStr nssListElement;
31
nssListCompareFunc compareFunc;
32
nssListSortFunc sortFunc;
33
PRBool i_alloced_arena;
36
struct nssListIteratorStr {
39
nssListElement *current;
42
#define NSSLIST_LOCK_IF(list) \
43
if ((list)->lock) PZ_Lock((list)->lock)
45
#define NSSLIST_UNLOCK_IF(list) \
46
if ((list)->lock) PZ_Unlock((list)->lock)
49
pointer_compare(void *a, void *b)
51
return (PRBool)(a == b);
54
static nssListElement *
55
nsslist_get_matching_element(nssList *list, void *data)
65
/* using a callback slows things down when it's just compare ... */
66
if (list->compareFunc(node->data, data)) {
70
if (link == PR_LIST_TAIL(&list->head->link)) {
74
node = (nssListElement *)PR_NEXT_LINK(&node->link);
79
NSS_IMPLEMENT nssList *
93
arena = nssArena_Create();
97
return (nssList *)NULL;
99
list = nss_ZNEW(arena, nssList);
102
NSSArena_Destroy(arena);
104
return (nssList *)NULL;
107
list->lock = PZ_NewLock(nssILockOther);
112
NSSArena_Destroy(arena);
114
return (nssList *)NULL;
118
list->i_alloced_arena = i_alloced;
119
list->compareFunc = pointer_compare;
123
NSS_IMPLEMENT PRStatus
124
nssList_Destroy(nssList *list)
126
if (!list->i_alloced_arena) {
127
nssList_Clear(list, NULL);
130
(void)PZ_DestroyLock(list->lock);
132
if (list->i_alloced_arena) {
133
NSSArena_Destroy(list->arena);
141
nssList_SetCompareFunction(nssList *list, nssListCompareFunc compareFunc)
143
list->compareFunc = compareFunc;
147
nssList_SetSortFunction(nssList *list, nssListSortFunc sortFunc)
149
/* XXX if list already has elements, sort them */
150
list->sortFunc = sortFunc;
153
NSS_IMPLEMENT nssListCompareFunc
154
nssList_GetCompareFunction(nssList *list)
156
return list->compareFunc;
160
nssList_Clear(nssList *list, nssListElementDestructorFunc destructor)
163
nssListElement *node, *tmp;
164
NSSLIST_LOCK_IF(list);
167
while (node && list->count > 0) {
168
if (destructor) (*destructor)(node->data);
170
tmp = (nssListElement *)PR_NEXT_LINK(link);
171
PR_REMOVE_LINK(link);
176
NSSLIST_UNLOCK_IF(list);
180
nsslist_add_element(nssList *list, void *data)
182
nssListElement *node = nss_ZNEW(list->arena, nssListElement);
186
PR_INIT_CLIST(&node->link);
189
if (list->sortFunc) {
191
nssListElement *currNode;
192
currNode = list->head;
193
/* insert in ordered list */
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;
203
if (link == PR_LIST_TAIL(&list->head->link)) {
204
/* reached end of list, append */
205
PR_INSERT_AFTER(&node->link, link);
208
currNode = (nssListElement *)PR_NEXT_LINK(&currNode->link);
212
PR_APPEND_LINK(&node->link, &list->head->link);
221
NSS_IMPLEMENT PRStatus
222
nssList_Add(nssList *list, void *data)
225
NSSLIST_LOCK_IF(list);
226
nssrv = nsslist_add_element(list, data);
227
NSSLIST_UNLOCK_IF(list);
231
NSS_IMPLEMENT PRStatus
232
nssList_AddUnique(nssList *list, void *data)
235
nssListElement *node;
236
NSSLIST_LOCK_IF(list);
237
node = nsslist_get_matching_element(list, data);
239
/* already in, finish */
240
NSSLIST_UNLOCK_IF(list);
243
nssrv = nsslist_add_element(list, data);
244
NSSLIST_UNLOCK_IF(list);
248
NSS_IMPLEMENT PRStatus
249
nssList_Remove(nssList *list, void *data)
251
nssListElement *node;
252
NSSLIST_LOCK_IF(list);
253
node = nsslist_get_matching_element(list, data);
255
if (node == list->head) {
256
list->head = (nssListElement *)PR_NEXT_LINK(&node->link);
258
PR_REMOVE_LINK(&node->link);
260
if (--list->count == 0) {
264
NSSLIST_UNLOCK_IF(list);
269
nssList_Get(nssList *list, void *data)
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;
278
NSS_IMPLEMENT PRUint32
279
nssList_Count(nssList *list)
284
NSS_IMPLEMENT PRStatus
285
nssList_GetArray(nssList *list, void **rvArray, PRUint32 maxElements)
287
nssListElement *node;
289
PR_ASSERT(maxElements > 0);
294
NSSLIST_LOCK_IF(list);
296
rvArray[i++] = node->data;
297
if (i == maxElements) break;
298
node = (nssListElement *)PR_NEXT_LINK(&node->link);
299
if (node == list->head) {
303
NSSLIST_UNLOCK_IF(list);
307
NSS_IMPLEMENT nssList *
308
nssList_Clone(nssList *list)
311
nssListElement *node;
312
rvList = nssList_Create(NULL, (list->lock != NULL));
316
NSSLIST_LOCK_IF(list);
317
if (list->count > 0) {
320
nssList_Add(rvList, node->data);
321
node = (nssListElement *)PR_NEXT_LINK(&node->link);
322
if (node == list->head) {
327
NSSLIST_UNLOCK_IF(list);
331
NSS_IMPLEMENT nssListIterator *
332
nssList_CreateIterator(nssList *list)
334
nssListIterator *rvIterator;
335
rvIterator = nss_ZNEW(NULL, nssListIterator);
339
rvIterator->list = nssList_Clone(list);
340
if (!rvIterator->list) {
341
nss_ZFreeIf(rvIterator);
344
rvIterator->current = rvIterator->list->head;
346
rvIterator->lock = PZ_NewLock(nssILockOther);
347
if (!rvIterator->lock) {
348
nssList_Destroy(rvIterator->list);
349
nss_ZFreeIf(rvIterator);
357
nssListIterator_Destroy(nssListIterator *iter)
360
(void)PZ_DestroyLock(iter->lock);
362
nssList_Destroy(iter->list);
367
nssListIterator_Start(nssListIterator *iter)
369
NSSLIST_LOCK_IF(iter);
370
if (iter->list->count == 0) {
373
iter->current = iter->list->head;
374
return iter->current->data;
378
nssListIterator_Next(nssListIterator *iter)
380
nssListElement *node;
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.
388
node = (nssListElement *)PR_NEXT_LINK(&iter->current->link);
390
if (link == PR_LIST_TAIL(&iter->list->head->link)) {
391
/* Signal the end of the list. */
392
iter->current = NULL;
395
iter->current = node;
399
NSS_IMPLEMENT PRStatus
400
nssListIterator_Finish(nssListIterator *iter)
402
iter->current = iter->list->head;
403
return (iter->lock) ? PZ_Unlock(iter->lock) : PR_SUCCESS;