[asterisk-commits] rizzo: branch rizzo/astobj2 r45885 - in /team/rizzo/astobj2: include/asterisk...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sun Oct 22 07:17:28 MST 2006


Author: rizzo
Date: Sun Oct 22 09:17:27 2006
New Revision: 45885

URL: http://svn.digium.com/view/asterisk?rev=45885&view=rev
Log:
implement buckets as queues with head and tail.
This is a very common usage for a container and there is
no significant gain in not doing that.

On passing, do a bunch of simplifications and name changes
to improve readability.


Modified:
    team/rizzo/astobj2/include/asterisk/astobj2.h
    team/rizzo/astobj2/main/astobj2.c

Modified: team/rizzo/astobj2/include/asterisk/astobj2.h
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/include/asterisk/astobj2.h?rev=45885&r1=45884&r2=45885&view=diff
==============================================================================
--- team/rizzo/astobj2/include/asterisk/astobj2.h (original)
+++ team/rizzo/astobj2/include/asterisk/astobj2.h Sun Oct 22 09:17:27 2006
@@ -325,7 +325,7 @@
  * \param c The container to investigate.
  * \return Zero if the container is empty.
  */
-int astobj2_container_empty(struct container *c);
+int astobj2_container_count(struct container *c);
 
 /*
  * Here we have functions to manage objects.
@@ -345,6 +345,7 @@
  * \note Remember to set the key before calling this function.
  */
 void *astobj2_link(struct container *c, void *newobj);
+void *astobj2_unlink(struct container *c, void *newobj);
 
 /*! \struct Used as return value if the flag OBJ_MULTIPLE is set */
 struct astobj2_list {

Modified: team/rizzo/astobj2/main/astobj2.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/main/astobj2.c?rev=45885&r1=45884&r2=45885&view=diff
==============================================================================
--- team/rizzo/astobj2/main/astobj2.c (original)
+++ team/rizzo/astobj2/main/astobj2.c Sun Oct 22 09:17:27 2006
@@ -23,8 +23,7 @@
 /* TODO: Revision: ? */
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-// #include <stdio.h>
-#include <unistd.h>	/* sleep */
+#include <unistd.h>	/* sleep, only for debugging */
 
 #include "asterisk/astobj2.h"
 #include "asterisk/utils.h"
@@ -37,7 +36,7 @@
  * The refcount is used to decide when it is time to
  * invoke the destructor.
  */
-struct astobj2_priv_data {
+struct __priv_data {
         ast_mutex_t     lock;
         int             ref_counter;
         astobj2_destructor_fn destructor_fn;
@@ -48,7 +47,7 @@
  * followed by variable-size user data.
  */
 struct astobj2 {
-        struct astobj2_priv_data priv_data;
+        struct __priv_data priv_data;
         void *user_data[0];
 };
 
@@ -103,7 +102,7 @@
 		sleep(1);
 	}
 
-	if ( current_value <= 0 ) { /* last reference, destroy the object */
+	if (current_value <= 0) { /* last reference, destroy the object */
 		ast_log(LOG_DEBUG, "refcount %d on object %p, we destroy the object\n", current_value, a);
 
 		if (obj->priv_data.destructor_fn != NULL) 
@@ -115,7 +114,7 @@
 		 * allocated.
 		 */
 		bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
-		free( obj );
+		free(obj);
 	}
 
 	return ret;
@@ -149,15 +148,29 @@
 
 /* internal callback to destroy a container. */
 static void container_destruct(void *c);
+
+/* each bucket in the container is a tailq. */
+struct bucket {
+	struct bucket_list *head;
+	struct bucket_list *tail;
+};
 
 struct container {
         astobj2_hash_fn hash_fn;
         astobj2_callback_fn cmp_fn;
         int     n_buckets;
         int     elements;       /* number of elements in the container */
-        struct astobj2_bucket_list *buckets[0]; /* variable size */
+        struct bucket buckets[0]; /* variable size */
 };
  
+/* it is convenient to have a hash function that always returns 0.
+ * This is basically used when we want to have a container that is
+ * a simple linked list.
+ */
+static int hash_zero(const void *obj, const int flags)
+{
+	return 0;
+}
 /*
  * A container is just an object, after all!
  */
@@ -165,27 +178,26 @@
 astobj2_container_alloc(const uint n_buckets, astobj2_hash_fn hash_fn,
 		astobj2_callback_fn cmp_fn)
 {
+	/* XXX maybe consistency check on arguments ? */
 	/* compute the container size */
-	size_t container_size = sizeof (struct container)
-		+ n_buckets * sizeof (struct astobj2_bucket_list *);
-
-	/* XXX must fix the destructor */
+	size_t container_size = sizeof(struct container) + n_buckets * sizeof(struct bucket);
+
 	struct container *c = astobj2_obj_alloc(container_size, container_destruct);
 
-	if ( c == NULL )
+	if (c == NULL)
 		return NULL;
 
 	/* init */
 	c->elements = 0;
 	c->n_buckets = n_buckets;
-	c->hash_fn = hash_fn;
+	c->hash_fn = hash_fn ? hash_fn : hash_zero;
 	c->cmp_fn = cmp_fn;
 
 	return c;
 }
 
 /* XXX better to keep a global count of elements */
-int astobj2_container_empty(struct container *c)
+int astobj2_container_count(struct container *c)
 {
 	return c->elements;
 }
@@ -195,51 +207,62 @@
  * used within a bucket.
  * XXX this should be private to the container code
  */
-struct astobj2_bucket_list {
-        struct astobj2_bucket_list *next;       /* pointer to next bucket_list */
+struct bucket_list {
+        struct bucket_list *next;       /* pointer to next bucket_list */
         struct astobj2 *obj;                    /* pointer to internal data */
 }; 
 
 /*!
  * link an object to a container
  */
-void *astobj2_link(struct container *c, void *newobj)
-{
-	struct astobj2_bucket_list *list_pointer;
-	uint i = c->hash_fn ? c->hash_fn( newobj, OBJ_POINTER ) : 0;
+void *astobj2_link(struct container *c, void *obj)
+{
+	int i;
+	/* create a new list entry */
+	struct bucket_list *p = ast_calloc(1, sizeof(struct bucket_list));
+
+	if (p == NULL)		/* alloc failure, die */
+		return NULL;
+	/* apply the hash function */
+	i = c->hash_fn(obj, OBJ_POINTER);
 
 	astobj2_lock(c);
 	i %= c->n_buckets;
-	list_pointer = ast_calloc( 1, sizeof (struct astobj2_bucket_list) );
-
-	/* linking the object to head */
-	if (list_pointer != NULL) {
-		list_pointer->next = c->buckets[i];
-		list_pointer->obj = INTERNAL_OBJ(newobj);
-		c->buckets[i] = list_pointer;
-		list_pointer = newobj;
-	}
+
+	p->next = NULL;
+	p->obj = INTERNAL_OBJ(obj);
+	if (c->buckets[i].head == NULL)
+		c->buckets[i].head = p;
+	else
+		c->buckets[i].tail->next = p;
+	c->buckets[i].tail = p;
+	ast_atomic_fetchadd_int(&c->elements, 1);
 	astobj2_unlock(c);
-	return list_pointer;
+	return p;
+}
+
+/*
+ * another convenience function is a callback that matches on address
+ */
+static int match_by_addr(void *obj, void *arg, int flags)
+{
+	return (obj == arg) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 /*! 
- * \internal
  * \brief Unlink an object from the list
  * and destroy the associated * astobj2_bucket_list structure.
  */
-static void obj_unlink(struct astobj2_bucket_list *prev_pointer)
-{
-	struct astobj2_bucket_list *to_delete = prev_pointer->next;
-
-	prev_pointer->next = to_delete->next;
-	free(to_delete);
+void *astobj2_unlink(struct container *c, void *arg)
+{
+	astobj2_callback(c, OBJ_SINGLE | OBJ_UNLINK | OBJ_POINTER, match_by_addr, arg);
+	return NULL;
 }
 
 /* special callback that matches all */
 static int cb_true(void *obj, void *arg, int flags)
 {
-	return 1;
+	return CMP_MATCH;
 }
 
 /*!
@@ -252,23 +275,30 @@
 	astobj2_callback_fn cb_fn, void *arg)
 {
 	int i, last;
-	int match;
 	struct astobj2 *ret = NULL;
 
 	if ( (flags & (OBJ_SINGLE | OBJ_DATA)) == OBJ_DATA) {
 		ast_log(LOG_WARNING, "multiple data return not implemented yet");
 		return NULL;
 	}
-	if (cb_fn == NULL)	/* if NULL, match everything */
+
+	/* override the match function if necessary */
+	if (flags & OBJ_POINTER)
+		cb_fn = match_by_addr;
+	else if (cb_fn == NULL)	/* if NULL, match everything */
 		cb_fn = cb_true;
 	/*
 	 * XXX this can be optimized.
+	 * If we have a hash function and lookup by pointer,
+	 * run the hash function. Otherwise, scan the whole container
+	 * (this only for the time being. We need to optimize this.)
 	 */
-	if (c->hash_fn && flags & OBJ_POINTER)	/* we know hash can handle this case */
+	if ((flags & OBJ_POINTER))	/* we know hash can handle this case */
 		i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
 	else			/* don't know, let's scan all buckets */
 		i = -1;		/* XXX this must be fixed later. */
 
+	/* determine the search boundaries: i..last-1 */
 	if (i < 0) {
 		i = 0;
 		last = c->n_buckets;
@@ -276,37 +306,41 @@
 		last = i+1;
 	}
 
-	astobj2_lock(c);
+	astobj2_lock(c);	/* avoid modifications to the content */
 
 	for(; i < last ; i++) {
-		struct astobj2_bucket_list *prev = NULL, *cur = c->buckets[i];
+		struct bucket_list *prev = NULL, *cur = c->buckets[i].head;
 		while (cur) {
-			/* match by pointer or exec a function */
-			if ( flags & OBJ_POINTER )
-				match = ( cur->obj == INTERNAL_OBJ(arg) );
-			else
-				match = cb_fn(EXTERNAL_OBJ(cur->obj), arg, flags );
+			int match = cb_fn(EXTERNAL_OBJ(cur->obj), arg, flags );
 
 			/* we found the object, performing operations according flags */
-			if (!match) {
+			if (match == 0) {	/* no match, no stop, continue */
 				prev = cur;
 				cur = cur->next;
 				continue;
+			} else if (match == CMP_STOP) {	/* we are done */
+				i = last;
+				break;
 			}
-			if (flags & OBJ_DATA)
+			/* we know CMP_MATCH is set here */
+			if (flags & OBJ_DATA)	/* must return the object, mark the value */
 				ret = cur->obj;
-			if (flags & OBJ_UNLINK) {
-				struct astobj2_bucket_list *x = cur;
-
-				if (prev == NULL)
-					c->buckets[i] = cur->next;
-				else
+			if (flags & OBJ_UNLINK) {	/* must unlink */
+				struct bucket_list *x = cur;
+
+				if (prev == NULL)	/* gone from head */
+					c->buckets[i].head  = cur->next;
+				else			/* gone from the middle */
 					prev->next = cur->next;
+				/* update tail if needed needed */
+				if (c->buckets[i].tail == cur)
+					c->buckets[i].tail = prev;
 				cur = cur->next ;
 				free(x);
+				ast_atomic_fetchadd_int(&c->elements, -1);
 			}
 
-			if (flags & OBJ_SINGLE) {
+			if ((match & CMP_STOP) || (flags & OBJ_SINGLE)) {
 				/* We found the only match we need */
 				i = last;	/* force exit from outer loop */
 				break;
@@ -368,7 +402,7 @@
 void * astobj2_iterator_next(struct astobj2_iterator *a)
 {
 	int lim;
-	struct astobj2_bucket_list **bp;
+	struct bucket *bp;
 
 	if (a->cur)
 		astobj2_ref(EXTERNAL_OBJ(a->cur), -1);
@@ -380,14 +414,14 @@
 	astobj2_lock(a->c);
 	lim = a->c->n_buckets;
 	bp = a->c->buckets;
-	
+
 	/* we browse the buckets array, moving to the next
 	 * buckets if we don't find the entry in the current one.
 	 */
 	for ( ; a->bucket < lim;
 			/* move to next bucket */
 			a->bucket++, a->pos = -1) {
-		struct astobj2_bucket_list *p = bp[a->bucket], *cand = NULL;
+		struct bucket_list *p = bp[a->bucket].head, *cand = NULL;
 		int i;
 
 		if (p == NULL)		/* empty bucket */



More information about the asterisk-commits mailing list