diff options
Diffstat (limited to 'ldap/servers/slapd/objset.c')
-rw-r--r-- | ldap/servers/slapd/objset.c | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/ldap/servers/slapd/objset.c b/ldap/servers/slapd/objset.c new file mode 100644 index 00000000..ceb5f225 --- /dev/null +++ b/ldap/servers/slapd/objset.c @@ -0,0 +1,380 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "slapi-plugin.h" +#include "slapi-private.h" + +/* + * The "wrapper" placed around objects in our object set. + */ +typedef struct objset_object +{ + Object *obj; /* pointer to actual object */ + struct objset_object *next; /* pointer to next object in list */ +} objset_object; + +/* + * The object set itself. + */ +typedef struct objset +{ + objset_object *head; /* pointer to linked list of objects */ + objset_object *tail; /* pointer to tail of linked list */ + PRLock *lock; /* Lock - protects addition/deletion from list */ + FNFree destructor; /* Destructor callback for objset itself */ +} objset; + +/* Forward declarations */ +static void unlinkObjsetObjectNoLock(Objset *o, objset_object *obj_to_unlink); + + +/* + * Create a new, empty object set. + * Returns a pointer to the new object set, or NULL if an error occurred. + */ +Objset * +objset_new(FNFree objset_destructor) +{ + objset *set; + + set = (objset *)slapi_ch_malloc(sizeof(objset)); + set->lock = PR_NewLock(); + if (NULL == set->lock) + { + slapi_ch_free((void **)&set); + } + else + { + set->head = set->tail = NULL; + set->destructor = objset_destructor; + } + return set; +} + + +/* + * Delete an object set. All references to contained objects + * are released, and the objset is deleted. + */ +void +objset_delete(Objset **setp) +{ + objset_object *o, *o_next; + Objset *set; + + PR_ASSERT(NULL != setp); + set = *setp; + PR_ASSERT(NULL != set); + PR_Lock(set->lock); + o = set->head; + while (NULL != o) + { + o_next = o->next; + object_release(o->obj); /* release our reference */ + slapi_ch_free((void **)&o); /* Free wrapper */ + o = o_next; + } + PR_Unlock(set->lock); + PR_DestroyLock(set->lock); + if (NULL != set->destructor) + { + set->destructor((void **)setp); + } + slapi_ch_free((void **)setp); +} + + + +/* + * Add a new object to an object set. + * Return values: + * OBJSET_SUCCESS: the insertion was succesful. + * OBJSET_ALREADY_EXISTS: the object already exists in the set. + */ +int +objset_add_obj(Objset *set, Object *object) +{ + objset_object *p; + int exists = 0; + int rc = OBJSET_SUCCESS; + + PR_ASSERT(NULL != set); + PR_ASSERT(NULL != object); + + PR_Lock(set->lock); + /* Make sure this object isn't already in the set */ + p = set->head; + while (NULL != p) + { + if (p->obj == object) + { + exists = 1; + break; + } + p = p->next; + } + if (exists) + { + rc = OBJSET_ALREADY_EXISTS; + } + else + { + objset_object *new_node = (objset_object *)slapi_ch_malloc(sizeof(objset_object)); + object_acquire(object); /* Record our reference */ + new_node->obj = object; + new_node->next = NULL; + + if (NULL == set->head) + { + set->head = set->tail = new_node; + } + else + { + set->tail->next = new_node; + set->tail = new_node; + } + } + PR_Unlock(set->lock); + return rc; +} + + +/* + * Locate an object in an object set. + * + * Arguments: + * set: the object set to search + * compare_fn: a caller-provided function used to compare the + * name against an object. This function should return + * a negtive value, zero, or a positive value if the + * object's name is, respectively, less that, equal + * to, or greater than the provided name. + * name: the name (value) to find. + * + * The returned object, if any, is referenced. The caller must + * call object_release() when finished with the object. + * + * Returns the object, if found. Otherwise, returns NULL. + * Implementation note: since we store objects in an unordered + * linked list, all that's really important is that the compare_fn + * return 0 when a match is found, and non-zero otherwise. + * Other types of implementations might try to optimize the + * storage, e.g. binary search. + */ +Object * +objset_find(Objset *set, CMPFn compare_fn, const void *name) +{ + objset_object *found = NULL; + + PR_ASSERT(NULL != set); + PR_ASSERT(NULL != name); + PR_ASSERT(NULL != compare_fn); + + PR_Lock(set->lock); + found = set->head; + while (NULL != found) + { + if (compare_fn(found->obj, name) == 0) + { + break; + } + found = found->next; + } + if (NULL != found) + { + /* acquire object */ + object_acquire(found->obj); + } + PR_Unlock(set->lock); + return found == NULL ? NULL : found->obj; +} + + + + +/* + * Remove an object from an objset. + * Returns OBJSET_SUCCESS if the object was found and removed, or + * OBJSET_NO_SUCH_OBJECT if the object was not found in the list. + */ +int +objset_remove_obj(Objset *set, Object *object) +{ + int rc = OBJSET_SUCCESS; + objset_object *found; + + PR_ASSERT(NULL != set); + PR_ASSERT(NULL != object); + + PR_Lock(set->lock); + found = set->head; + while (NULL != found) + { + if (found->obj == object) + { + break; + } + found = found->next; + } + if (NULL == found) + { + rc = OBJSET_NO_SUCH_OBJECT; + } + else + { + Object *saved = found->obj; + + /* Unlink from list */ + unlinkObjsetObjectNoLock(set, found); + + /* Release reference on object */ + object_release(saved); + } + PR_Unlock(set->lock); + return rc; +} + + + +/* + * Prepare for iteration across an object set. Returns the first + * object in the set. The returned object is referenced, therefore + * the caller must either release the object, or + * pass it to an objset_next_obj call, which will + * implicitly release the object. + * Returns the first object, or NULL if the objset contains no + * objects. + */ +Object * +objset_first_obj(Objset *set) +{ + Object *return_object; + + /* Be tolerant (for the replication plugin) */ + if (set == NULL) return NULL; + + PR_Lock(set->lock); + if (NULL == set->head) + { + return_object = NULL; + } + else + { + object_acquire(set->head->obj); + return_object = set->head->obj; + } + PR_Unlock(set->lock); + return return_object; +} + + +/* + * Return the next object in an object set, or NULL if there are + * no more objects. + * The returned object is referenced, therefore + * the caller must either release the object, or + * pass it to an objset_next_ob call, which will + * implicitly release the object. + */ +Object * +objset_next_obj(Objset *set, Object *previous) +{ + Object *return_object = NULL; + objset_object *p; + + PR_ASSERT(NULL != set); + PR_Lock(set->lock); + + /* First, find the current object */ + p = set->head; + while (NULL != p && p->obj != previous) + { + p = p->next; + } + /* Find the next object */ + if (NULL != p && NULL != p->next) + { + return_object = p->next->obj; + object_acquire(return_object); + } + PR_Unlock(set->lock); + object_release(previous); /* Release the previous object */ + return return_object; +} + + + +/* + * Return a non-zero value if the object set is empty, or + * zero if the object set is empty. + */ +int +objset_is_empty(Objset *set) +{ + int return_value; + + PR_ASSERT(NULL != set); + + PR_Lock(set->lock); + return_value = (set->head == NULL); + PR_Unlock(set->lock); + return return_value; +} + + + +/* + * Return the count of objects in the object set. + */ +int objset_size(Objset *set) +{ + int count = 0; + objset_object *p; + + PR_ASSERT(NULL != set); + PR_Lock(set->lock); + for (p = set->head; p; p = p->next) + count++; + PR_Unlock(set->lock); + return count; +} + + + +/* + * Utility function: remove object from list. + * This needs to be called with the objset locked. + */ +static void +unlinkObjsetObjectNoLock(Objset *o, objset_object *obj_to_unlink) +{ + + objset_object *p = o->head; + + PR_ASSERT(NULL != o->head); + /* Unlink from list */ + if (o->head == obj_to_unlink) { + /* Object to unlink was at head of list */ + p = o->head->next; + o->head = obj_to_unlink->next; + } else { + while (NULL != p->next && p->next != obj_to_unlink) { + p = p->next; + } + if (NULL != p->next) + { + /* p points to object prior to one being removed */ + p->next = p->next->next; + } + } + if (o->tail == obj_to_unlink) + { + o->tail = p; + } + + /* Free the wrapper */ + slapi_ch_free((void **)&obj_to_unlink); +} |