1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
38 static const char CVS_ID
[] = "@(#) $RCSfile: list.c,v $ $Revision: 1.20 $ $Date: 2006/09/29 20:13:30 $";
44 * This contains the implementation of NSS's thread-safe linked list.
51 struct nssListElementStr
{
56 typedef struct nssListElementStr nssListElement
;
63 nssListCompareFunc compareFunc
;
64 nssListSortFunc sortFunc
;
65 PRBool i_alloced_arena
;
68 struct nssListIteratorStr
{
71 nssListElement
*current
;
74 #define NSSLIST_LOCK_IF(list) \
75 if ((list)->lock) PZ_Lock((list)->lock)
77 #define NSSLIST_UNLOCK_IF(list) \
78 if ((list)->lock) PZ_Unlock((list)->lock)
81 pointer_compare(void *a
, void *b
)
83 return (PRBool
)(a
== b
);
86 static nssListElement
*
87 nsslist_get_matching_element(nssList
*list
, void *data
)
97 /* using a callback slows things down when it's just compare ... */
98 if (list
->compareFunc(node
->data
, data
)) {
102 if (link
== PR_LIST_TAIL(&list
->head
->link
)) {
106 node
= (nssListElement
*)PR_NEXT_LINK(&node
->link
);
111 NSS_IMPLEMENT nssList
*
123 i_alloced
= PR_FALSE
;
125 arena
= nssArena_Create();
129 return (nssList
*)NULL
;
131 list
= nss_ZNEW(arena
, nssList
);
134 NSSArena_Destroy(arena
);
136 return (nssList
*)NULL
;
139 list
->lock
= PZ_NewLock(nssILockOther
);
144 NSSArena_Destroy(arena
);
146 return (nssList
*)NULL
;
150 list
->i_alloced_arena
= i_alloced
;
151 list
->compareFunc
= pointer_compare
;
155 NSS_IMPLEMENT PRStatus
156 nssList_Destroy(nssList
*list
)
158 if (!list
->i_alloced_arena
) {
159 nssList_Clear(list
, NULL
);
162 (void)PZ_DestroyLock(list
->lock
);
164 if (list
->i_alloced_arena
) {
165 NSSArena_Destroy(list
->arena
);
173 nssList_SetCompareFunction(nssList
*list
, nssListCompareFunc compareFunc
)
175 list
->compareFunc
= compareFunc
;
179 nssList_SetSortFunction(nssList
*list
, nssListSortFunc sortFunc
)
181 /* XXX if list already has elements, sort them */
182 list
->sortFunc
= sortFunc
;
185 NSS_IMPLEMENT nssListCompareFunc
186 nssList_GetCompareFunction(nssList
*list
)
188 return list
->compareFunc
;
192 nssList_Clear(nssList
*list
, nssListElementDestructorFunc destructor
)
195 nssListElement
*node
, *tmp
;
196 NSSLIST_LOCK_IF(list
);
199 while (node
&& list
->count
> 0) {
200 if (destructor
) (*destructor
)(node
->data
);
202 tmp
= (nssListElement
*)PR_NEXT_LINK(link
);
203 PR_REMOVE_LINK(link
);
208 NSSLIST_UNLOCK_IF(list
);
212 nsslist_add_element(nssList
*list
, void *data
)
214 nssListElement
*node
= nss_ZNEW(list
->arena
, nssListElement
);
218 PR_INIT_CLIST(&node
->link
);
221 if (list
->sortFunc
) {
223 nssListElement
*currNode
;
224 currNode
= list
->head
;
225 /* insert in ordered list */
227 link
= &currNode
->link
;
228 if (list
->sortFunc(data
, currNode
->data
) <= 0) {
229 /* new element goes before current node */
230 PR_INSERT_BEFORE(&node
->link
, link
);
231 /* reset head if this is first */
232 if (currNode
== list
->head
) list
->head
= node
;
235 if (link
== PR_LIST_TAIL(&list
->head
->link
)) {
236 /* reached end of list, append */
237 PR_INSERT_AFTER(&node
->link
, link
);
240 currNode
= (nssListElement
*)PR_NEXT_LINK(&currNode
->link
);
244 PR_APPEND_LINK(&node
->link
, &list
->head
->link
);
253 NSS_IMPLEMENT PRStatus
254 nssList_Add(nssList
*list
, void *data
)
257 NSSLIST_LOCK_IF(list
);
258 nssrv
= nsslist_add_element(list
, data
);
259 NSSLIST_UNLOCK_IF(list
);
263 NSS_IMPLEMENT PRStatus
264 nssList_AddUnique(nssList
*list
, void *data
)
267 nssListElement
*node
;
268 NSSLIST_LOCK_IF(list
);
269 node
= nsslist_get_matching_element(list
, data
);
271 /* already in, finish */
272 NSSLIST_UNLOCK_IF(list
);
275 nssrv
= nsslist_add_element(list
, data
);
276 NSSLIST_UNLOCK_IF(list
);
280 NSS_IMPLEMENT PRStatus
281 nssList_Remove(nssList
*list
, void *data
)
283 nssListElement
*node
;
284 NSSLIST_LOCK_IF(list
);
285 node
= nsslist_get_matching_element(list
, data
);
287 if (node
== list
->head
) {
288 list
->head
= (nssListElement
*)PR_NEXT_LINK(&node
->link
);
290 PR_REMOVE_LINK(&node
->link
);
292 if (--list
->count
== 0) {
296 NSSLIST_UNLOCK_IF(list
);
301 nssList_Get(nssList
*list
, void *data
)
303 nssListElement
*node
;
304 NSSLIST_LOCK_IF(list
);
305 node
= nsslist_get_matching_element(list
, data
);
306 NSSLIST_UNLOCK_IF(list
);
307 return (node
) ? node
->data
: NULL
;
310 NSS_IMPLEMENT PRUint32
311 nssList_Count(nssList
*list
)
316 NSS_IMPLEMENT PRStatus
317 nssList_GetArray(nssList
*list
, void **rvArray
, PRUint32 maxElements
)
319 nssListElement
*node
;
321 PR_ASSERT(maxElements
> 0);
326 NSSLIST_LOCK_IF(list
);
328 rvArray
[i
++] = node
->data
;
329 if (i
== maxElements
) break;
330 node
= (nssListElement
*)PR_NEXT_LINK(&node
->link
);
331 if (node
== list
->head
) {
335 NSSLIST_UNLOCK_IF(list
);
339 NSS_IMPLEMENT nssList
*
340 nssList_Clone(nssList
*list
)
343 nssListElement
*node
;
344 rvList
= nssList_Create(NULL
, (list
->lock
!= NULL
));
348 NSSLIST_LOCK_IF(list
);
349 if (list
->count
> 0) {
352 nssList_Add(rvList
, node
->data
);
353 node
= (nssListElement
*)PR_NEXT_LINK(&node
->link
);
354 if (node
== list
->head
) {
359 NSSLIST_UNLOCK_IF(list
);
363 NSS_IMPLEMENT nssListIterator
*
364 nssList_CreateIterator(nssList
*list
)
366 nssListIterator
*rvIterator
;
367 rvIterator
= nss_ZNEW(NULL
, nssListIterator
);
371 rvIterator
->list
= nssList_Clone(list
);
372 if (!rvIterator
->list
) {
373 nss_ZFreeIf(rvIterator
);
376 rvIterator
->current
= rvIterator
->list
->head
;
378 rvIterator
->lock
= PZ_NewLock(nssILockOther
);
379 if (!rvIterator
->lock
) {
380 nssList_Destroy(rvIterator
->list
);
381 nss_ZFreeIf(rvIterator
);
389 nssListIterator_Destroy(nssListIterator
*iter
)
392 (void)PZ_DestroyLock(iter
->lock
);
394 nssList_Destroy(iter
->list
);
399 nssListIterator_Start(nssListIterator
*iter
)
401 NSSLIST_LOCK_IF(iter
);
402 if (iter
->list
->count
== 0) {
405 iter
->current
= iter
->list
->head
;
406 return iter
->current
->data
;
410 nssListIterator_Next(nssListIterator
*iter
)
412 nssListElement
*node
;
414 if (iter
->list
->count
== 1 || iter
->current
== NULL
) {
415 /* Reached the end of the list. Don't change the state, force to
416 * user to call nssList_Finish to clean up.
420 node
= (nssListElement
*)PR_NEXT_LINK(&iter
->current
->link
);
422 if (link
== PR_LIST_TAIL(&iter
->list
->head
->link
)) {
423 /* Signal the end of the list. */
424 iter
->current
= NULL
;
427 iter
->current
= node
;
431 NSS_IMPLEMENT PRStatus
432 nssListIterator_Finish(nssListIterator
*iter
)
434 iter
->current
= iter
->list
->head
;
435 return (iter
->lock
) ? PZ_Unlock(iter
->lock
) : PR_SUCCESS
;