/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* We implement these like arrays, but we keep them sorted by key.
* This allows us to binary-search as well as keep externalized output
* sane-looking for human eyes.
*/
#define EXPAND_STEP 16
/*
* prop_dictionary_keysym_t is allocated with space at the end to hold the
* key. This must be a regular object so that we can maintain sane iterator
* semantics -- we don't want to require that the caller release the result
* of prop_object_iterator_next().
*
* We'd like to have some small'ish keysym objects for up-to-16 characters
* in a key, some for up-to-32 characters in a key, and then a final bucket
* for up-to-128 characters in a key (not including NUL). Keys longer than
* 128 characters are not allowed.
*/
struct _prop_dictionary_keysym {
struct _prop_object pdk_obj;
size_t pdk_size;
struct rb_node pdk_link;
char pdk_key[1];
/* actually variable length */
};
struct _prop_dictionary_iterator {
struct _prop_object_iterator pdi_base;
unsigned int pdi_index;
};
/*
* Dictionary key symbols are immutable, and we are likely to have many
* duplicated key symbols. So, to save memory, we unique'ify key symbols
* so we only have to have one copy of each string.
*/
if (! (prop_object_is_dictionary_keysym(pdk1) &&
prop_object_is_dictionary_keysym(pdk2)))
return (FALSE);
/*
* 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);
}
/*
* Check to see if this already exists in the tree. If it does,
* we just retain it and return it.
*/
_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
if (! _prop_dict_keysym_tree_initialized) {
_prop_rb_tree_init(&_prop_dict_keysym_tree,
&_prop_dict_keysym_rb_tree_ops);
_prop_dict_keysym_tree_initialized = TRUE;
} else {
n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
if (n != NULL) {
opdk = RBNODE_TO_PDK(n);
prop_object_retain(opdk);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
return (opdk);
}
}
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
/*
* We dropped the mutex when we allocated the new object, so
* we have to check again if it is in the tree.
*/
_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
if (n != NULL) {
opdk = RBNODE_TO_PDK(n);
prop_object_retain(opdk);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
_prop_dict_keysym_put(pdk);
return (opdk);
}
_prop_rb_tree_insert_node(&_prop_dict_keysym_tree, &pdk->pdk_link);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
return (pdk);
}
/*
* prop_dictionary_create_with_capacity --
* Create a dictionary with the capacity to store N objects.
*/
prop_dictionary_t
prop_dictionary_create_with_capacity(unsigned int capacity)
{
return (_prop_dictionary_alloc(capacity));
}
/*
* prop_dictionary_copy --
* Copy a dictionary. The new dictionary has an initial capacity equal
* to the number of objects stored int the original dictionary. The new
* dictionary contains refrences to the original dictionary's objects,
* not copies of those objects (i.e. a shallow copy).
*/
prop_dictionary_t
prop_dictionary_copy(prop_dictionary_t opd)
{
prop_dictionary_t pd;
prop_dictionary_keysym_t pdk;
prop_object_t po;
unsigned int idx;
if (! prop_object_is_dictionary(opd))
return (NULL);
_PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
pd = _prop_dictionary_alloc(opd->pd_count);
if (pd != NULL) {
for (idx = 0; idx < opd->pd_count; idx++) {
pdk = opd->pd_array[idx].pde_key;
po = opd->pd_array[idx].pde_objref;
/*
* prop_dictionary_copy_mutable --
* Like prop_dictionary_copy(), but the resulting dictionary is
* mutable.
*/
prop_dictionary_t
prop_dictionary_copy_mutable(prop_dictionary_t opd)
{
prop_dictionary_t pd;
if (! prop_object_is_dictionary(opd))
return (NULL);
pd = prop_dictionary_copy(opd);
if (pd != NULL)
pd->pd_flags &= ~PD_F_IMMUTABLE;
return (pd);
}
/*
* prop_dictionary_count --
* Return the number of objects stored in the dictionary.
*/
unsigned int
prop_dictionary_count(prop_dictionary_t pd)
{
unsigned int rv;
/*
* prop_dictionary_ensure_capacity --
* Ensure that the dictionary has the capacity to store the specified
* total number of objects (including the objects already stored in
* the dictionary).
*/
boolean_t
prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
{
boolean_t rv;
if (! prop_object_is_dictionary(pd))
return (FALSE);
/*
* prop_dictionary_iterator --
* Return an iterator for the dictionary. The dictionary is retained by
* the iterator.
*/
prop_object_iterator_t
prop_dictionary_iterator(prop_dictionary_t pd)
{
struct _prop_dictionary_iterator *pdi;
if (! prop_object_is_dictionary(pd))
return (NULL);
/*
* prop_dictionary_all_keys --
* Return an array containing a snapshot of all of the keys
* in the dictionary.
*/
prop_array_t
prop_dictionary_all_keys(prop_dictionary_t pd)
{
prop_array_t array;
unsigned int idx;
boolean_t rv = TRUE;
if (! prop_object_is_dictionary(pd))
return (NULL);
/* There is no pressing need to lock the dictionary for this. */
array = prop_array_create_with_capacity(pd->pd_count);
_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
for (idx = 0; idx < pd->pd_count; idx++) {
rv = prop_array_add(array, pd->pd_array[idx].pde_key);
if (rv == FALSE)
break;
}
/*
* prop_dictionary_get_keysym --
* Return the object stored at the location encoded by the keysym.
*/
prop_object_t
prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
{
if (! (prop_object_is_dictionary(pd) &&
prop_object_is_dictionary_keysym(pdk)))
return (NULL);
return (prop_dictionary_get(pd, pdk->pdk_key));
}
/*
* prop_dictionary_set --
* Store a reference to an object at with the specified key.
* If the key already exisit, the original object is released.
*/
boolean_t
prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
{
struct _prop_dict_entry *pde;
prop_dictionary_keysym_t pdk;
unsigned int idx;
boolean_t rv = FALSE;
if (! prop_object_is_dictionary(pd))
return (FALSE);
_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
if (prop_dictionary_is_immutable(pd))
return (FALSE);
if (strcmp(key, pde->pde_key->pdk_key) < 0) {
/*
* key < 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(&pd->pd_array[1], &pd->pd_array[0],
pd->pd_count * sizeof(*pde));
pd->pd_array[0].pde_key = pdk;
pd->pd_array[0].pde_objref = po;
pd->pd_count++;
pd->pd_version++;
rv = TRUE;
goto out;
}
idx--;
}
/*
* prop_dictionary_set_keysym --
* Replace the object in the dictionary at the location encoded by
* the keysym.
*/
boolean_t
prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
prop_object_t po)
{
if (! (prop_object_is_dictionary(pd) &&
prop_object_is_dictionary_keysym(pdk)))
return (FALSE);
/*
* prop_dictionary_remove --
* Remove the reference to an object with the specified key from
* the dictionary.
*/
void
prop_dictionary_remove(prop_dictionary_t pd, const char *key)
{
struct _prop_dict_entry *pde;
unsigned int idx;
if (! prop_object_is_dictionary(pd))
return;
_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
/* XXX Should this be a _PROP_ASSERT()? */
if (prop_dictionary_is_immutable(pd))
goto out;
pde = _prop_dict_lookup(pd, key, &idx);
/* XXX Should this be a _PROP_ASSERT()? */
if (pde == NULL)
goto out;
/*
* prop_dictionary_remove_keysym --
* Remove a reference to an object stored in the dictionary at the
* location encoded by the keysym.
*/
void
prop_dictionary_remove_keysym(prop_dictionary_t pd,
prop_dictionary_keysym_t pdk)
{
if (! (prop_object_is_dictionary(pd) &&
prop_object_is_dictionary_keysym(pdk)))
return;
prop_dictionary_remove(pd, pdk->pdk_key);
}
/*
* prop_dictionary_equals --
* Return TRUE if the two dictionaries are equivalent. Note we do a
* by-value comparison of the objects in the dictionary.
*/
boolean_t
prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
{
return (_prop_dictionary_equals(dict1, dict2));
}
/*
* prop_dictionary_keysym_cstring_nocopy --
* Return an immutable reference to the keysym's value.
*/
const char *
prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
{
if (! prop_object_is_dictionary_keysym(pdk))
return (NULL);
return (pdk->pdk_key);
}
/*
* prop_dictionary_keysym_equals --
* Return TRUE if the two dictionary key symbols are equivalent.
* Note: We do not compare the object references.
*/
boolean_t
prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
prop_dictionary_keysym_t pdk2)
{
return (_prop_dict_keysym_equals(pdk1, pdk2));
}
#if !defined(_KERNEL) && !defined(_STANDALONE)
/*
* prop_dictionary_externalize_to_file --
* Externalize a dictionary to the specified file.
*/
boolean_t
prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
{
char *xml;
boolean_t rv;
int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */
xml = prop_dictionary_externalize(dict);
if (xml == NULL)
return (FALSE);
rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
if (rv == FALSE)
save_errno = errno;
_PROP_FREE(xml, M_TEMP);
if (rv == FALSE)
errno = save_errno;
return (rv);
}
/*
* prop_dictionary_internalize_from_file --
* Internalize a dictionary from a file.
*/
prop_dictionary_t
prop_dictionary_internalize_from_file(const char *fname)
{
struct _prop_object_internalize_mapped_file *mf;
prop_dictionary_t dict;