Use a red-black tree to unique'ify the dictionary key symbols, rather than

a sorted array.  Using a tree puts the linkage into the key symbol itself,
allowing us to avoid having to allocate memory with a mutex held.
This commit is contained in:
thorpej 2006-07-07 17:09:36 +00:00
parent 656024b633
commit 296508b606
1 changed files with 45 additions and 145 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_dictionary.c,v 1.7 2006/05/28 10:15:25 jnemeth Exp $ */
/* $NetBSD: prop_dictionary.c,v 1.8 2006/07/07 17:09:36 thorpej Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -40,6 +40,12 @@
#include <prop/prop_string.h>
#include "prop_object_impl.h"
#if defined(__NetBSD__)
#include <sys/tree.h>
#else
#error Need to find a NetBSD sys/tree.h
#endif
/*
* We implement these like arrays, but we keep them sorted by key.
* This allows us to binary-search as well as keep externalized output
@ -62,6 +68,7 @@
struct _prop_dictionary_keysym {
struct _prop_object pdk_obj;
size_t pdk_size;
RB_ENTRY(_prop_dictionary_keysym) pdk_link;
char pdk_key[1];
/* actually variable length */
};
@ -143,67 +150,23 @@ struct _prop_dictionary_iterator {
* duplicated key symbols. So, to save memory, we unique'ify key symbols
* so we only have to have one copy of each string.
*/
static struct {
prop_dictionary_keysym_t *pdkt_array;
unsigned int pdkt_count;
unsigned int pdkt_capacity;
} _prop_dict_keysym_table;
_PROP_MUTEX_DECL(_prop_dict_keysym_table_mutex)
static boolean_t
_prop_dict_keysym_table_expand(void)
static int
_prop_dict_keysym_tree_cmp(prop_dictionary_keysym_t pdk1,
prop_dictionary_keysym_t pdk2)
{
prop_dictionary_keysym_t *array, *oarray;
unsigned int capacity;
oarray = _prop_dict_keysym_table.pdkt_array;
capacity = _prop_dict_keysym_table.pdkt_capacity + EXPAND_STEP;
array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
if (array == NULL)
return (FALSE);
if (oarray != NULL)
memcpy(array, oarray,
_prop_dict_keysym_table.pdkt_capacity * sizeof(*array));
_prop_dict_keysym_table.pdkt_array = array;
_prop_dict_keysym_table.pdkt_capacity = capacity;
if (oarray != NULL)
_PROP_FREE(oarray, M_PROP_DICT);
return (TRUE);
return (strcmp(pdk1->pdk_key, pdk2->pdk_key));
}
static prop_dictionary_keysym_t
_prop_dict_keysym_lookup(const char *key, unsigned int *idxp)
{
prop_dictionary_keysym_t pdk;
unsigned int base, idx, distance;
int res;
static RB_HEAD(_prop_dict_keysym_tree, _prop_dictionary_keysym)
_prop_dict_keysym_tree = RB_INITIALIZER(&_prop_dict_keysym_tree);
RB_PROTOTYPE(_prop_dict_keysym_tree, _prop_dictionary_keysym, pdk_link,
_prop_dict_keysym_tree_cmp)
RB_GENERATE(_prop_dict_keysym_tree, _prop_dictionary_keysym, pdk_link,
_prop_dict_keysym_tree_cmp)
for (idx = 0, base = 0, distance = _prop_dict_keysym_table.pdkt_count;
distance != 0; distance >>= 1) {
idx = base + (distance >> 1);
pdk = _prop_dict_keysym_table.pdkt_array[idx];
_PROP_ASSERT(pdk != NULL);
res = strcmp(key, pdk->pdk_key);
if (res == 0) {
if (idxp != NULL)
*idxp = idx;
return (pdk);
}
if (res > 0) { /* key > pdk_key: move right */
base = idx + 1;
distance--;
} /* else move left */
}
/* idx points to the slot we look at last. */
if (idxp != NULL)
*idxp = idx;
return (NULL);
}
_PROP_MUTEX_DECL(_prop_dict_keysym_tree_mutex)
static void
_prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
@ -223,18 +186,10 @@ static void
_prop_dict_keysym_free(void *v)
{
prop_dictionary_keysym_t pdk = v;
prop_dictionary_keysym_t opdk;
unsigned int idx;
_PROP_MUTEX_LOCK(_prop_dict_keysym_table_mutex);
opdk = _prop_dict_keysym_lookup(pdk->pdk_key, &idx);
_PROP_ASSERT(pdk == opdk);
idx++;
memmove(&_prop_dict_keysym_table.pdkt_array[idx - 1],
&_prop_dict_keysym_table.pdkt_array[idx],
(_prop_dict_keysym_table.pdkt_count - idx) * sizeof(pdk));
_prop_dict_keysym_table.pdkt_count--;
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_table_mutex);
_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
RB_REMOVE(_prop_dict_keysym_tree, &_prop_dict_keysym_tree, pdk);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
_prop_dict_keysym_put(pdk);
}
@ -266,9 +221,12 @@ _prop_dict_keysym_equals(void *v1, void *v2)
_PROP_ASSERT(prop_object_is_dictionary_keysym(pdk1));
_PROP_ASSERT(prop_object_is_dictionary_keysym(pdk2));
if (pdk1 == pdk2)
return (TRUE);
return (strcmp(pdk1->pdk_key, pdk2->pdk_key) == 0);
/*
* There is only ever one copy of a keysym at any given time,
* so we can reduce this to a simple pointer equality check.
*/
return (pdk1 == pdk2);
}
static prop_dictionary_keysym_t
@ -276,23 +234,11 @@ _prop_dict_keysym_alloc(const char *key)
{
prop_dictionary_keysym_t opdk, pdk;
size_t size;
unsigned int idx;
/*
* See if this key is already in the keysym table. If so,
* retain the existing object and return it.
*/
_PROP_MUTEX_LOCK(_prop_dict_keysym_table_mutex);
opdk = _prop_dict_keysym_lookup(key, NULL);
if (opdk != NULL) {
prop_object_retain(opdk);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_table_mutex);
return (opdk);
}
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_table_mutex);
/*
* Not in the table. Create a new one.
* Because of the way our RB trees work, we need to create the
* new keysym in order to check if it's already in the tree.
* Oh well.
*/
size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */;
@ -304,76 +250,30 @@ _prop_dict_keysym_alloc(const char *key)
else if (size <= PDK_SIZE_128)
pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
else
return (NULL); /* key too long */
pdk = NULL; /* key too long */
if (pdk != NULL) {
_prop_object_init(&pdk->pdk_obj,
&_prop_object_type_dict_keysym);
if (pdk == NULL)
return (NULL);
strcpy(pdk->pdk_key, key);
pdk->pdk_size = size;
}
_prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym);
strcpy(pdk->pdk_key, key);
pdk->pdk_size = size;
/*
* Before we return it, we need to insert it into the table.
* But, because we dropped the mutex, we need to see if someone
* beat us to it.
* Now check to see if this already exists in the tree. If it
* does, we return a reference to the existing one and free the
* new one we just created.
*/
_PROP_MUTEX_LOCK(_prop_dict_keysym_table_mutex);
opdk = _prop_dict_keysym_lookup(key, &idx);
_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
opdk = RB_INSERT(_prop_dict_keysym_tree, &_prop_dict_keysym_tree, pdk);
if (opdk != NULL) {
prop_object_retain(opdk);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_table_mutex);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
_prop_dict_keysym_put(pdk);
return (opdk);
}
if (_prop_dict_keysym_table.pdkt_count ==
_prop_dict_keysym_table.pdkt_capacity &&
_prop_dict_keysym_table_expand() == FALSE) {
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_table_mutex);
prop_object_release(pdk);
return (NULL);
}
opdk = _prop_dict_keysym_table.pdkt_array[idx];
if (_prop_dict_keysym_table.pdkt_count == 0) {
_prop_dict_keysym_table.pdkt_array[0] = pdk;
_prop_dict_keysym_table.pdkt_count++;
goto out;
}
if (strcmp(key, opdk->pdk_key) < 0) {
/*
* key < opdk->pdk_key: insert to the left. This is the
* same as inserting to the right, except we decrement
* the current index first.
*
* Because we're unsigned, we have to special case 0
* (grumble).
*/
if (idx == 0) {
memmove(&_prop_dict_keysym_table.pdkt_array[1],
&_prop_dict_keysym_table.pdkt_array[0],
_prop_dict_keysym_table.pdkt_count *
sizeof(pdk));
_prop_dict_keysym_table.pdkt_array[0] = pdk;
_prop_dict_keysym_table.pdkt_count++;
goto out;
}
idx--;
}
memmove(&_prop_dict_keysym_table.pdkt_array[idx + 2],
&_prop_dict_keysym_table.pdkt_array[idx + 1],
(_prop_dict_keysym_table.pdkt_count - (idx + 1)) *
sizeof(pdk));
_prop_dict_keysym_table.pdkt_array[idx + 1] = pdk;
_prop_dict_keysym_table.pdkt_count++;
out:
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_table_mutex);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
return (pdk);
}