Coverage Report

Created: 2025-07-01 06:25

/src/nss/lib/base/list.c
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
}