[cairo-commit] cairo/src cairo-hash-private.h, 1.2, 1.3 cairo-hash.c, 1.15, 1.16

Carl Worth commit at pdx.freedesktop.org
Wed Jun 29 17:02:39 PDT 2005


Committed by: cworth

Update of /cvs/cairo/cairo/src
In directory gabe:/tmp/cvs-serv28366/src

Modified Files:
	cairo-hash-private.h cairo-hash.c 
Log Message:

        * src/cairo-hash-private.h:
        * src/cairo-hash.c (_cairo_hash_table_random_entry): Add
        _cairo_hash_table_random_entry.

        * src/cairo-hash.c: (_destroy_entry): Fix to update live_entries.

        * src/cairo-hash.c: (_cairo_hash_table_lookup_internal): style
        changes.

        * src/cairo-hash.c (_cairo_hash_table_resize): Add code to shrink
        table as well as to grow it.

        * src/cairo-hash.c (_cairo_hash_table_insert),
        (_cairo_hash_table_remove): Call new version of resize so that
        table will grow or shrink as needed on insert and remove.


Index: cairo-hash-private.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-hash-private.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- cairo-hash-private.h	29 Jun 2005 22:02:11 -0000	1.2
+++ cairo-hash-private.h	30 Jun 2005 00:02:37 -0000	1.3
@@ -97,11 +97,14 @@
 			  cairo_hash_entry_t  *key,
 			  cairo_hash_entry_t **entry_return);
 
+cairo_private void *
+_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table);
+
 cairo_private cairo_status_t
 _cairo_hash_table_insert (cairo_hash_table_t *hash_table,
 			  cairo_hash_entry_t *entry);
 
-cairo_private void
+cairo_private cairo_status_t
 _cairo_hash_table_remove (cairo_hash_table_t *hash_table,
 			  cairo_hash_entry_t *key);
 

Index: cairo-hash.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-hash.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- cairo-hash.c	29 Jun 2005 22:02:11 -0000	1.15
+++ cairo-hash.c	30 Jun 2005 00:02:37 -0000	1.16
@@ -174,9 +174,13 @@
 static void
 _destroy_entry (cairo_hash_table_t *hash_table, cairo_hash_entry_t **entry)
 {
+    if (! ENTRY_IS_LIVE (*entry))
+	return;
+
     if (hash_table->entry_destroy)
 	hash_table->entry_destroy (*entry);
     *entry = DEAD_ENTRY;
+    hash_table->live_entries--;
 }
 
 /**
@@ -250,11 +254,11 @@
     {
 	entry = &hash_table->entries[idx];
 
-	if (*entry == NULL)
+	if (ENTRY_IS_FREE(*entry))
 	{
 	    return entry;
 	}
-	else if (*entry == DEAD_ENTRY)
+	else if (ENTRY_IS_DEAD(*entry))
 	{
 	    if (key_is_unique) {
 		return entry;
@@ -263,7 +267,7 @@
 		    first_available = entry;
 	    }
 	}
-	else /* LIVE */
+	else /* ENTRY_IS_LIVE(*entry) */
 	{
 	    if (! key_is_unique)
 		if (hash_table->keys_equal (key, *entry))
@@ -290,22 +294,51 @@
     return first_available;
 }
 
+/**
+ * _cairo_hash_table_resize:
+ * @hash_table: a hash table
+ * 
+ * Resize the hash table if the number of entries has gotten much
+ * bigger or smaller than the ideal number of entries for the current
+ * size.
+ * 
+ * Return value: CAIRO_STATUS_SUCCESS if successful or
+ * CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
 static cairo_status_t
-_cairo_hash_table_resize (cairo_hash_table_t *hash_table,
-			  unsigned long       proposed_size)
+_cairo_hash_table_resize  (cairo_hash_table_t *hash_table)
 {
     cairo_hash_table_t tmp;
     cairo_hash_entry_t **entry;
     unsigned long new_size, i;
 
-    if (hash_table->arrangement->high_water_mark >= proposed_size)
+    /* This keeps the hash table between 25% and 50% full. */
+    unsigned long high = hash_table->arrangement->high_water_mark;
+    unsigned long low = high >> 2;
+
+    if (hash_table->live_entries >= low && hash_table->live_entries <= high)
 	return CAIRO_STATUS_SUCCESS;
 
     tmp = *hash_table;
 
-    tmp.arrangement = hash_table->arrangement + 1;
-    /* This code is being abused if we can't make a table big enough. */
-    assert (tmp.arrangement - hash_table_arrangements < NUM_HASH_TABLE_ARRANGEMENTS);
+    if (hash_table->live_entries > high)
+    {
+	tmp.arrangement = hash_table->arrangement + 1;
+	/* This code is being abused if we can't make a table big enough. */
+	assert (tmp.arrangement - hash_table_arrangements <
+		NUM_HASH_TABLE_ARRANGEMENTS);
+	fprintf (stderr, "Growing from %ld to %ld\n",
+		 hash_table->arrangement->size, tmp.arrangement->size);
+    }
+    else /* hash_table->live_entries < low */
+    {
+	/* Can't shrink if we're at the smallest size */
+	if (hash_table->arrangement == &hash_table_arrangements[0])
+	    return CAIRO_STATUS_SUCCESS;
+	tmp.arrangement = hash_table->arrangement - 1;
+	fprintf (stderr, "Shrinking from %ld to %ld\n",
+		 hash_table->arrangement->size, tmp.arrangement->size);
+    }
 
     new_size = tmp.arrangement->size;
     tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*));
@@ -321,9 +354,11 @@
 	    *entry = hash_table->entries[i];
 	}
     }
+
     free (hash_table->entries);
     hash_table->entries = tmp.entries;
     hash_table->arrangement = tmp.arrangement;
+
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -360,6 +395,59 @@
 }
 
 /**
+ * _cairo_hash_table_random_entry:
+ * @hash_table: a hash table
+ * 
+ * Find a random entry in the hash table.
+ *
+ * We use the same algorithm as the lookup algorithm to walk over the
+ * entries in the hash table in a pseudo-random order. Walking
+ * linearly would favor entries following gaps in the hash table. We
+ * could also call rand() repeatedly, which works well for almost-full
+ * tables, but degrades when the table is almost empty, or predicate
+ * returns false for most entries.
+ *
+ * NOTE: It'd be really easy to turn this into a find function with a
+ * predicate if anybody might want that.
+ * 
+ * Return value: a random live entry or NULL if there are no live
+ * entries.
+ **/
+void *
+_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table)
+{
+    cairo_hash_entry_t **entry;
+    unsigned long hash;
+    unsigned long table_size, i, idx, step;
+
+    table_size = hash_table->arrangement->size;
+
+    hash = rand ();
+    idx = hash % table_size;
+    step = 0;
+
+    for (i = 0; i < table_size; ++i)
+    {
+	entry = &hash_table->entries[idx];
+
+	if (ENTRY_IS_LIVE (*entry))
+	    return *entry;
+
+	if (step == 0) { 	    
+	    step = hash % hash_table->arrangement->rehash;
+	    if (step == 0)
+		step = 1;
+	}
+
+	idx += step;
+	if (idx >= table_size)
+	    idx -= table_size;
+    }
+
+    return NULL;
+}
+
+/**
  * _cairo_hash_table_insert:
  * @hash_table: a hash table
  * @key_and_value: an entry to be inserted
@@ -379,14 +467,6 @@
     cairo_status_t status;
     cairo_hash_entry_t **entry;
     
-    /* Ensure there is room in the table in case we need to add a new
-     * entry. */
-    status = _cairo_hash_table_resize (hash_table,
-				       hash_table->live_entries + 1);
-    if (status != CAIRO_STATUS_SUCCESS) {
-	return status;
-    }
-
     entry = _cairo_hash_table_lookup_internal (hash_table,
 					       key_and_value, FALSE);
     
@@ -403,7 +483,11 @@
 	hash_table->live_entries++;
     }
 
-    return status;
+    status = _cairo_hash_table_resize (hash_table);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 /**
@@ -414,16 +498,28 @@
  * Remove an entry from the hash table which has a key that matches
  * @key, (as determined by the keys_equal() function passed to
  * _cairo_hash_table_create), if any.
+ *
+ * Return value: CAIRO_STATUS_SUCCESS if successful or
+ * CAIRO_STATUS_NO_MEMORY if out of memory.
  **/
-void
+cairo_status_t
 _cairo_hash_table_remove (cairo_hash_table_t *hash_table,
 			  cairo_hash_entry_t *key)
 {
+    cairo_status_t status;
     cairo_hash_entry_t **entry;
 
     entry = _cairo_hash_table_lookup_internal (hash_table, key, FALSE);
-    if (ENTRY_IS_LIVE(*entry))
-	_destroy_entry (hash_table, entry);
+    if (! ENTRY_IS_LIVE(*entry))
+	return CAIRO_STATUS_SUCCESS;
+
+    _destroy_entry (hash_table, entry);
+
+    status = _cairo_hash_table_resize (hash_table);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 /**




More information about the cairo-commit mailing list