mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-16 04:20:32 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
401 lines
8.6 KiB
C
401 lines
8.6 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* list.c
|
|
*
|
|
* This contains the implementation of NSS's thread-safe linked list.
|
|
*/
|
|
|
|
#ifndef BASE_H
|
|
#include "base.h"
|
|
#endif /* BASE_H */
|
|
|
|
struct nssListElementStr {
|
|
PRCList link;
|
|
void *data;
|
|
};
|
|
|
|
typedef struct nssListElementStr nssListElement;
|
|
|
|
struct nssListStr {
|
|
NSSArena *arena;
|
|
PZLock *lock;
|
|
nssListElement *head;
|
|
PRUint32 count;
|
|
nssListCompareFunc compareFunc;
|
|
nssListSortFunc sortFunc;
|
|
PRBool i_alloced_arena;
|
|
};
|
|
|
|
struct nssListIteratorStr {
|
|
PZLock *lock;
|
|
nssList *list;
|
|
nssListElement *current;
|
|
};
|
|
|
|
#define NSSLIST_LOCK_IF(list) \
|
|
if ((list)->lock) PZ_Lock((list)->lock)
|
|
|
|
#define NSSLIST_UNLOCK_IF(list) \
|
|
if ((list)->lock) PZ_Unlock((list)->lock)
|
|
|
|
static PRBool
|
|
pointer_compare(void *a, void *b)
|
|
{
|
|
return (PRBool)(a == b);
|
|
}
|
|
|
|
static nssListElement *
|
|
nsslist_get_matching_element(nssList *list, void *data)
|
|
{
|
|
PRCList *link;
|
|
nssListElement *node;
|
|
node = list->head;
|
|
if (!node) {
|
|
return NULL;
|
|
}
|
|
link = &node->link;
|
|
while (node) {
|
|
/* using a callback slows things down when it's just compare ... */
|
|
if (list->compareFunc(node->data, data)) {
|
|
break;
|
|
}
|
|
link = &node->link;
|
|
if (link == PR_LIST_TAIL(&list->head->link)) {
|
|
node = NULL;
|
|
break;
|
|
}
|
|
node = (nssListElement *)PR_NEXT_LINK(&node->link);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
NSS_IMPLEMENT nssList *
|
|
nssList_Create
|
|
(
|
|
NSSArena *arenaOpt,
|
|
PRBool threadSafe
|
|
)
|
|
{
|
|
NSSArena *arena;
|
|
nssList *list;
|
|
PRBool i_alloced;
|
|
if (arenaOpt) {
|
|
arena = arenaOpt;
|
|
i_alloced = PR_FALSE;
|
|
} else {
|
|
arena = nssArena_Create();
|
|
i_alloced = PR_TRUE;
|
|
}
|
|
if (!arena) {
|
|
return (nssList *)NULL;
|
|
}
|
|
list = nss_ZNEW(arena, nssList);
|
|
if (!list) {
|
|
if (!arenaOpt) {
|
|
NSSArena_Destroy(arena);
|
|
}
|
|
return (nssList *)NULL;
|
|
}
|
|
if (threadSafe) {
|
|
list->lock = PZ_NewLock(nssILockOther);
|
|
if (!list->lock) {
|
|
if (arenaOpt) {
|
|
nss_ZFreeIf(list);
|
|
} else {
|
|
NSSArena_Destroy(arena);
|
|
}
|
|
return (nssList *)NULL;
|
|
}
|
|
}
|
|
list->arena = arena;
|
|
list->i_alloced_arena = i_alloced;
|
|
list->compareFunc = pointer_compare;
|
|
return list;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssList_Destroy(nssList *list)
|
|
{
|
|
if (!list->i_alloced_arena) {
|
|
nssList_Clear(list, NULL);
|
|
}
|
|
if (list->lock) {
|
|
(void)PZ_DestroyLock(list->lock);
|
|
}
|
|
if (list->i_alloced_arena) {
|
|
NSSArena_Destroy(list->arena);
|
|
list = NULL;
|
|
}
|
|
nss_ZFreeIf(list);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
NSS_IMPLEMENT void
|
|
nssList_SetCompareFunction(nssList *list, nssListCompareFunc compareFunc)
|
|
{
|
|
list->compareFunc = compareFunc;
|
|
}
|
|
|
|
NSS_IMPLEMENT void
|
|
nssList_SetSortFunction(nssList *list, nssListSortFunc sortFunc)
|
|
{
|
|
/* XXX if list already has elements, sort them */
|
|
list->sortFunc = sortFunc;
|
|
}
|
|
|
|
NSS_IMPLEMENT nssListCompareFunc
|
|
nssList_GetCompareFunction(nssList *list)
|
|
{
|
|
return list->compareFunc;
|
|
}
|
|
|
|
NSS_IMPLEMENT void
|
|
nssList_Clear(nssList *list, nssListElementDestructorFunc destructor)
|
|
{
|
|
PRCList *link;
|
|
nssListElement *node, *tmp;
|
|
NSSLIST_LOCK_IF(list);
|
|
node = list->head;
|
|
list->head = NULL;
|
|
while (node && list->count > 0) {
|
|
if (destructor) (*destructor)(node->data);
|
|
link = &node->link;
|
|
tmp = (nssListElement *)PR_NEXT_LINK(link);
|
|
PR_REMOVE_LINK(link);
|
|
nss_ZFreeIf(node);
|
|
node = tmp;
|
|
--list->count;
|
|
}
|
|
NSSLIST_UNLOCK_IF(list);
|
|
}
|
|
|
|
static PRStatus
|
|
nsslist_add_element(nssList *list, void *data)
|
|
{
|
|
nssListElement *node = nss_ZNEW(list->arena, nssListElement);
|
|
if (!node) {
|
|
return PR_FAILURE;
|
|
}
|
|
PR_INIT_CLIST(&node->link);
|
|
node->data = data;
|
|
if (list->head) {
|
|
if (list->sortFunc) {
|
|
PRCList *link;
|
|
nssListElement *currNode;
|
|
currNode = list->head;
|
|
/* insert in ordered list */
|
|
while (currNode) {
|
|
link = &currNode->link;
|
|
if (list->sortFunc(data, currNode->data) <= 0) {
|
|
/* new element goes before current node */
|
|
PR_INSERT_BEFORE(&node->link, link);
|
|
/* reset head if this is first */
|
|
if (currNode == list->head) list->head = node;
|
|
break;
|
|
}
|
|
if (link == PR_LIST_TAIL(&list->head->link)) {
|
|
/* reached end of list, append */
|
|
PR_INSERT_AFTER(&node->link, link);
|
|
break;
|
|
}
|
|
currNode = (nssListElement *)PR_NEXT_LINK(&currNode->link);
|
|
}
|
|
} else {
|
|
/* not sorting */
|
|
PR_APPEND_LINK(&node->link, &list->head->link);
|
|
}
|
|
} else {
|
|
list->head = node;
|
|
}
|
|
++list->count;
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssList_Add(nssList *list, void *data)
|
|
{
|
|
NSSLIST_LOCK_IF(list);
|
|
(void)nsslist_add_element(list, data);
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssList_AddUnique(nssList *list, void *data)
|
|
{
|
|
PRStatus nssrv;
|
|
nssListElement *node;
|
|
NSSLIST_LOCK_IF(list);
|
|
node = nsslist_get_matching_element(list, data);
|
|
if (node) {
|
|
/* already in, finish */
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return PR_SUCCESS;
|
|
}
|
|
nssrv = nsslist_add_element(list, data);
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return nssrv;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssList_Remove(nssList *list, void *data)
|
|
{
|
|
nssListElement *node;
|
|
NSSLIST_LOCK_IF(list);
|
|
node = nsslist_get_matching_element(list, data);
|
|
if (node) {
|
|
if (node == list->head) {
|
|
list->head = (nssListElement *)PR_NEXT_LINK(&node->link);
|
|
}
|
|
PR_REMOVE_LINK(&node->link);
|
|
nss_ZFreeIf(node);
|
|
if (--list->count == 0) {
|
|
list->head = NULL;
|
|
}
|
|
}
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
NSS_IMPLEMENT void *
|
|
nssList_Get(nssList *list, void *data)
|
|
{
|
|
nssListElement *node;
|
|
NSSLIST_LOCK_IF(list);
|
|
node = nsslist_get_matching_element(list, data);
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return (node) ? node->data : NULL;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRUint32
|
|
nssList_Count(nssList *list)
|
|
{
|
|
return list->count;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssList_GetArray(nssList *list, void **rvArray, PRUint32 maxElements)
|
|
{
|
|
nssListElement *node;
|
|
PRUint32 i = 0;
|
|
PR_ASSERT(maxElements > 0);
|
|
node = list->head;
|
|
if (!node) {
|
|
return PR_SUCCESS;
|
|
}
|
|
NSSLIST_LOCK_IF(list);
|
|
while (node) {
|
|
rvArray[i++] = node->data;
|
|
if (i == maxElements) break;
|
|
node = (nssListElement *)PR_NEXT_LINK(&node->link);
|
|
if (node == list->head) {
|
|
break;
|
|
}
|
|
}
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
NSS_IMPLEMENT nssList *
|
|
nssList_Clone(nssList *list)
|
|
{
|
|
nssList *rvList;
|
|
nssListElement *node;
|
|
rvList = nssList_Create(NULL, (list->lock != NULL));
|
|
if (!rvList) {
|
|
return NULL;
|
|
}
|
|
NSSLIST_LOCK_IF(list);
|
|
if (list->count > 0) {
|
|
node = list->head;
|
|
while (PR_TRUE) {
|
|
nssList_Add(rvList, node->data);
|
|
node = (nssListElement *)PR_NEXT_LINK(&node->link);
|
|
if (node == list->head) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
NSSLIST_UNLOCK_IF(list);
|
|
return rvList;
|
|
}
|
|
|
|
NSS_IMPLEMENT nssListIterator *
|
|
nssList_CreateIterator(nssList *list)
|
|
{
|
|
nssListIterator *rvIterator;
|
|
rvIterator = nss_ZNEW(NULL, nssListIterator);
|
|
if (!rvIterator) {
|
|
return NULL;
|
|
}
|
|
rvIterator->list = nssList_Clone(list);
|
|
if (!rvIterator->list) {
|
|
nss_ZFreeIf(rvIterator);
|
|
return NULL;
|
|
}
|
|
rvIterator->current = rvIterator->list->head;
|
|
if (list->lock) {
|
|
rvIterator->lock = PZ_NewLock(nssILockOther);
|
|
if (!rvIterator->lock) {
|
|
nssList_Destroy(rvIterator->list);
|
|
nss_ZFreeIf(rvIterator);
|
|
rvIterator = NULL;
|
|
}
|
|
}
|
|
return rvIterator;
|
|
}
|
|
|
|
NSS_IMPLEMENT void
|
|
nssListIterator_Destroy(nssListIterator *iter)
|
|
{
|
|
if (iter->lock) {
|
|
(void)PZ_DestroyLock(iter->lock);
|
|
}
|
|
nssList_Destroy(iter->list);
|
|
nss_ZFreeIf(iter);
|
|
}
|
|
|
|
NSS_IMPLEMENT void *
|
|
nssListIterator_Start(nssListIterator *iter)
|
|
{
|
|
NSSLIST_LOCK_IF(iter);
|
|
if (iter->list->count == 0) {
|
|
return NULL;
|
|
}
|
|
iter->current = iter->list->head;
|
|
return iter->current->data;
|
|
}
|
|
|
|
NSS_IMPLEMENT void *
|
|
nssListIterator_Next(nssListIterator *iter)
|
|
{
|
|
nssListElement *node;
|
|
PRCList *link;
|
|
if (iter->list->count == 1 || iter->current == NULL) {
|
|
/* Reached the end of the list. Don't change the state, force to
|
|
* user to call nssList_Finish to clean up.
|
|
*/
|
|
return NULL;
|
|
}
|
|
node = (nssListElement *)PR_NEXT_LINK(&iter->current->link);
|
|
link = &node->link;
|
|
if (link == PR_LIST_TAIL(&iter->list->head->link)) {
|
|
/* Signal the end of the list. */
|
|
iter->current = NULL;
|
|
return node->data;
|
|
}
|
|
iter->current = node;
|
|
return node->data;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssListIterator_Finish(nssListIterator *iter)
|
|
{
|
|
iter->current = iter->list->head;
|
|
return (iter->lock) ? PZ_Unlock(iter->lock) : PR_SUCCESS;
|
|
}
|
|
|