summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/objset.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/objset.c')
-rw-r--r--ldap/servers/slapd/objset.c380
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);
+}