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