andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: * list.c andre@0: * andre@0: * This contains the implementation of NSS's thread-safe linked list. andre@0: */ andre@0: andre@0: #ifndef BASE_H andre@0: #include "base.h" andre@0: #endif /* BASE_H */ andre@0: andre@0: struct nssListElementStr { andre@0: PRCList link; andre@0: void *data; andre@0: }; andre@0: andre@0: typedef struct nssListElementStr nssListElement; andre@0: andre@0: struct nssListStr { andre@0: NSSArena *arena; andre@0: PZLock *lock; andre@0: nssListElement *head; andre@0: PRUint32 count; andre@0: nssListCompareFunc compareFunc; andre@0: nssListSortFunc sortFunc; andre@0: PRBool i_alloced_arena; andre@0: }; andre@0: andre@0: struct nssListIteratorStr { andre@0: PZLock *lock; andre@0: nssList *list; andre@0: nssListElement *current; andre@0: }; andre@0: andre@0: #define NSSLIST_LOCK_IF(list) \ andre@0: if ((list)->lock) PZ_Lock((list)->lock) andre@0: andre@0: #define NSSLIST_UNLOCK_IF(list) \ andre@0: if ((list)->lock) PZ_Unlock((list)->lock) andre@0: andre@0: static PRBool andre@0: pointer_compare(void *a, void *b) andre@0: { andre@0: return (PRBool)(a == b); andre@0: } andre@0: andre@0: static nssListElement * andre@0: nsslist_get_matching_element(nssList *list, void *data) andre@0: { andre@0: PRCList *link; andre@0: nssListElement *node; andre@0: node = list->head; andre@0: if (!node) { andre@0: return NULL; andre@0: } andre@0: link = &node->link; andre@0: while (node) { andre@0: /* using a callback slows things down when it's just compare ... */ andre@0: if (list->compareFunc(node->data, data)) { andre@0: break; andre@0: } andre@0: link = &node->link; andre@0: if (link == PR_LIST_TAIL(&list->head->link)) { andre@0: node = NULL; andre@0: break; andre@0: } andre@0: node = (nssListElement *)PR_NEXT_LINK(&node->link); andre@0: } andre@0: return node; andre@0: } andre@0: andre@0: NSS_IMPLEMENT nssList * andre@0: nssList_Create andre@0: ( andre@0: NSSArena *arenaOpt, andre@0: PRBool threadSafe andre@0: ) andre@0: { andre@0: NSSArena *arena; andre@0: nssList *list; andre@0: PRBool i_alloced; andre@0: if (arenaOpt) { andre@0: arena = arenaOpt; andre@0: i_alloced = PR_FALSE; andre@0: } else { andre@0: arena = nssArena_Create(); andre@0: i_alloced = PR_TRUE; andre@0: } andre@0: if (!arena) { andre@0: return (nssList *)NULL; andre@0: } andre@0: list = nss_ZNEW(arena, nssList); andre@0: if (!list) { andre@0: if (!arenaOpt) { andre@0: NSSArena_Destroy(arena); andre@0: } andre@0: return (nssList *)NULL; andre@0: } andre@0: if (threadSafe) { andre@0: list->lock = PZ_NewLock(nssILockOther); andre@0: if (!list->lock) { andre@0: if (arenaOpt) { andre@0: nss_ZFreeIf(list); andre@0: } else { andre@0: NSSArena_Destroy(arena); andre@0: } andre@0: return (nssList *)NULL; andre@0: } andre@0: } andre@0: list->arena = arena; andre@0: list->i_alloced_arena = i_alloced; andre@0: list->compareFunc = pointer_compare; andre@0: return list; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssList_Destroy(nssList *list) andre@0: { andre@0: if (!list->i_alloced_arena) { andre@0: nssList_Clear(list, NULL); andre@0: } andre@0: if (list->lock) { andre@0: (void)PZ_DestroyLock(list->lock); andre@0: } andre@0: if (list->i_alloced_arena) { andre@0: NSSArena_Destroy(list->arena); andre@0: list = NULL; andre@0: } andre@0: nss_ZFreeIf(list); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssList_SetCompareFunction(nssList *list, nssListCompareFunc compareFunc) andre@0: { andre@0: list->compareFunc = compareFunc; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssList_SetSortFunction(nssList *list, nssListSortFunc sortFunc) andre@0: { andre@0: /* XXX if list already has elements, sort them */ andre@0: list->sortFunc = sortFunc; andre@0: } andre@0: andre@0: NSS_IMPLEMENT nssListCompareFunc andre@0: nssList_GetCompareFunction(nssList *list) andre@0: { andre@0: return list->compareFunc; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssList_Clear(nssList *list, nssListElementDestructorFunc destructor) andre@0: { andre@0: PRCList *link; andre@0: nssListElement *node, *tmp; andre@0: NSSLIST_LOCK_IF(list); andre@0: node = list->head; andre@0: list->head = NULL; andre@0: while (node && list->count > 0) { andre@0: if (destructor) (*destructor)(node->data); andre@0: link = &node->link; andre@0: tmp = (nssListElement *)PR_NEXT_LINK(link); andre@0: PR_REMOVE_LINK(link); andre@0: nss_ZFreeIf(node); andre@0: node = tmp; andre@0: --list->count; andre@0: } andre@0: NSSLIST_UNLOCK_IF(list); andre@0: } andre@0: andre@0: static PRStatus andre@0: nsslist_add_element(nssList *list, void *data) andre@0: { andre@0: nssListElement *node = nss_ZNEW(list->arena, nssListElement); andre@0: if (!node) { andre@0: return PR_FAILURE; andre@0: } andre@0: PR_INIT_CLIST(&node->link); andre@0: node->data = data; andre@0: if (list->head) { andre@0: if (list->sortFunc) { andre@0: PRCList *link; andre@0: nssListElement *currNode; andre@0: currNode = list->head; andre@0: /* insert in ordered list */ andre@0: while (currNode) { andre@0: link = &currNode->link; andre@0: if (list->sortFunc(data, currNode->data) <= 0) { andre@0: /* new element goes before current node */ andre@0: PR_INSERT_BEFORE(&node->link, link); andre@0: /* reset head if this is first */ andre@0: if (currNode == list->head) list->head = node; andre@0: break; andre@0: } andre@0: if (link == PR_LIST_TAIL(&list->head->link)) { andre@0: /* reached end of list, append */ andre@0: PR_INSERT_AFTER(&node->link, link); andre@0: break; andre@0: } andre@0: currNode = (nssListElement *)PR_NEXT_LINK(&currNode->link); andre@0: } andre@0: } else { andre@0: /* not sorting */ andre@0: PR_APPEND_LINK(&node->link, &list->head->link); andre@0: } andre@0: } else { andre@0: list->head = node; andre@0: } andre@0: ++list->count; andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssList_Add(nssList *list, void *data) andre@0: { andre@0: PRStatus nssrv; andre@0: NSSLIST_LOCK_IF(list); andre@0: nssrv = nsslist_add_element(list, data); andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssList_AddUnique(nssList *list, void *data) andre@0: { andre@0: PRStatus nssrv; andre@0: nssListElement *node; andre@0: NSSLIST_LOCK_IF(list); andre@0: node = nsslist_get_matching_element(list, data); andre@0: if (node) { andre@0: /* already in, finish */ andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return PR_SUCCESS; andre@0: } andre@0: nssrv = nsslist_add_element(list, data); andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return nssrv; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssList_Remove(nssList *list, void *data) andre@0: { andre@0: nssListElement *node; andre@0: NSSLIST_LOCK_IF(list); andre@0: node = nsslist_get_matching_element(list, data); andre@0: if (node) { andre@0: if (node == list->head) { andre@0: list->head = (nssListElement *)PR_NEXT_LINK(&node->link); andre@0: } andre@0: PR_REMOVE_LINK(&node->link); andre@0: nss_ZFreeIf(node); andre@0: if (--list->count == 0) { andre@0: list->head = NULL; andre@0: } andre@0: } andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void * andre@0: nssList_Get(nssList *list, void *data) andre@0: { andre@0: nssListElement *node; andre@0: NSSLIST_LOCK_IF(list); andre@0: node = nsslist_get_matching_element(list, data); andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return (node) ? node->data : NULL; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRUint32 andre@0: nssList_Count(nssList *list) andre@0: { andre@0: return list->count; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssList_GetArray(nssList *list, void **rvArray, PRUint32 maxElements) andre@0: { andre@0: nssListElement *node; andre@0: PRUint32 i = 0; andre@0: PR_ASSERT(maxElements > 0); andre@0: node = list->head; andre@0: if (!node) { andre@0: return PR_SUCCESS; andre@0: } andre@0: NSSLIST_LOCK_IF(list); andre@0: while (node) { andre@0: rvArray[i++] = node->data; andre@0: if (i == maxElements) break; andre@0: node = (nssListElement *)PR_NEXT_LINK(&node->link); andre@0: if (node == list->head) { andre@0: break; andre@0: } andre@0: } andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT nssList * andre@0: nssList_Clone(nssList *list) andre@0: { andre@0: nssList *rvList; andre@0: nssListElement *node; andre@0: rvList = nssList_Create(NULL, (list->lock != NULL)); andre@0: if (!rvList) { andre@0: return NULL; andre@0: } andre@0: NSSLIST_LOCK_IF(list); andre@0: if (list->count > 0) { andre@0: node = list->head; andre@0: while (PR_TRUE) { andre@0: nssList_Add(rvList, node->data); andre@0: node = (nssListElement *)PR_NEXT_LINK(&node->link); andre@0: if (node == list->head) { andre@0: break; andre@0: } andre@0: } andre@0: } andre@0: NSSLIST_UNLOCK_IF(list); andre@0: return rvList; andre@0: } andre@0: andre@0: NSS_IMPLEMENT nssListIterator * andre@0: nssList_CreateIterator(nssList *list) andre@0: { andre@0: nssListIterator *rvIterator; andre@0: rvIterator = nss_ZNEW(NULL, nssListIterator); andre@0: if (!rvIterator) { andre@0: return NULL; andre@0: } andre@0: rvIterator->list = nssList_Clone(list); andre@0: if (!rvIterator->list) { andre@0: nss_ZFreeIf(rvIterator); andre@0: return NULL; andre@0: } andre@0: rvIterator->current = rvIterator->list->head; andre@0: if (list->lock) { andre@0: rvIterator->lock = PZ_NewLock(nssILockOther); andre@0: if (!rvIterator->lock) { andre@0: nssList_Destroy(rvIterator->list); andre@0: nss_ZFreeIf(rvIterator); andre@0: rvIterator = NULL; andre@0: } andre@0: } andre@0: return rvIterator; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssListIterator_Destroy(nssListIterator *iter) andre@0: { andre@0: if (iter->lock) { andre@0: (void)PZ_DestroyLock(iter->lock); andre@0: } andre@0: nssList_Destroy(iter->list); andre@0: nss_ZFreeIf(iter); andre@0: } andre@0: andre@0: NSS_IMPLEMENT void * andre@0: nssListIterator_Start(nssListIterator *iter) andre@0: { andre@0: NSSLIST_LOCK_IF(iter); andre@0: if (iter->list->count == 0) { andre@0: return NULL; andre@0: } andre@0: iter->current = iter->list->head; andre@0: return iter->current->data; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void * andre@0: nssListIterator_Next(nssListIterator *iter) andre@0: { andre@0: nssListElement *node; andre@0: PRCList *link; andre@0: if (iter->list->count == 1 || iter->current == NULL) { andre@0: /* Reached the end of the list. Don't change the state, force to andre@0: * user to call nssList_Finish to clean up. andre@0: */ andre@0: return NULL; andre@0: } andre@0: node = (nssListElement *)PR_NEXT_LINK(&iter->current->link); andre@0: link = &node->link; andre@0: if (link == PR_LIST_TAIL(&iter->list->head->link)) { andre@0: /* Signal the end of the list. */ andre@0: iter->current = NULL; andre@0: return node->data; andre@0: } andre@0: iter->current = node; andre@0: return node->data; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssListIterator_Finish(nssListIterator *iter) andre@0: { andre@0: iter->current = iter->list->head; andre@0: return (iter->lock) ? PZ_Unlock(iter->lock) : PR_SUCCESS; andre@0: } andre@0: