Home | History | Annotate | Line # | Download | only in libprop
prop_dictionary.c revision 1.42
      1 /*	$NetBSD: prop_dictionary.c,v 1.42 2020/06/06 21:25:59 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006, 2007, 2020 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "prop_object_impl.h"
     33 #include <prop/prop_array.h>
     34 #include <prop/prop_dictionary.h>
     35 #include <prop/prop_string.h>
     36 
     37 #include <sys/rbtree.h>
     38 
     39 #if !defined(_KERNEL) && !defined(_STANDALONE)
     40 #include <errno.h>
     41 #endif
     42 
     43 /*
     44  * We implement these like arrays, but we keep them sorted by key.
     45  * This allows us to binary-search as well as keep externalized output
     46  * sane-looking for human eyes.
     47  */
     48 
     49 #define	EXPAND_STEP		16
     50 
     51 /*
     52  * prop_dictionary_keysym_t is allocated with space at the end to hold the
     53  * key.  This must be a regular object so that we can maintain sane iterator
     54  * semantics -- we don't want to require that the caller release the result
     55  * of prop_object_iterator_next().
     56  *
     57  * We'd like to have some small'ish keysym objects for up-to-16 characters
     58  * in a key, some for up-to-32 characters in a key, and then a final bucket
     59  * for up-to-128 characters in a key (not including NUL).  Keys longer than
     60  * 128 characters are not allowed.
     61  */
     62 struct _prop_dictionary_keysym {
     63 	struct _prop_object		pdk_obj;
     64 	size_t				pdk_size;
     65 	struct rb_node			pdk_link;
     66 	char 				pdk_key[1];
     67 	/* actually variable length */
     68 };
     69 
     70 	/* pdk_key[1] takes care of the NUL */
     71 #define	PDK_SIZE_16		(sizeof(struct _prop_dictionary_keysym) + 16)
     72 #define	PDK_SIZE_32		(sizeof(struct _prop_dictionary_keysym) + 32)
     73 #define	PDK_SIZE_128		(sizeof(struct _prop_dictionary_keysym) + 128)
     74 
     75 #define	PDK_MAXKEY		128
     76 
     77 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16")
     78 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32")
     79 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128")
     80 
     81 struct _prop_dict_entry {
     82 	prop_dictionary_keysym_t	pde_key;
     83 	prop_object_t			pde_objref;
     84 };
     85 
     86 struct _prop_dictionary {
     87 	struct _prop_object	pd_obj;
     88 	_PROP_RWLOCK_DECL(pd_rwlock)
     89 	struct _prop_dict_entry	*pd_array;
     90 	unsigned int		pd_capacity;
     91 	unsigned int		pd_count;
     92 	int			pd_flags;
     93 
     94 	uint32_t		pd_version;
     95 };
     96 
     97 #define	PD_F_IMMUTABLE		0x01	/* dictionary is immutable */
     98 
     99 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
    100 		"propdict")
    101 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
    102 		    "property dictionary container object")
    103 
    104 static _prop_object_free_rv_t
    105 		_prop_dictionary_free(prop_stack_t, prop_object_t *);
    106 static void	_prop_dictionary_emergency_free(prop_object_t);
    107 static bool	_prop_dictionary_externalize(
    108 				struct _prop_object_externalize_context *,
    109 				void *);
    110 static _prop_object_equals_rv_t
    111 		_prop_dictionary_equals(prop_object_t, prop_object_t,
    112 				        void **, void **,
    113 					prop_object_t *, prop_object_t *);
    114 static void	_prop_dictionary_equals_finish(prop_object_t, prop_object_t);
    115 static prop_object_iterator_t
    116 		_prop_dictionary_iterator_locked(prop_dictionary_t);
    117 static prop_object_t
    118 		_prop_dictionary_iterator_next_object_locked(void *);
    119 static prop_object_t
    120 		_prop_dictionary_get_keysym(prop_dictionary_t,
    121 					    prop_dictionary_keysym_t, bool);
    122 static prop_object_t
    123 		_prop_dictionary_get(prop_dictionary_t, const char *, bool);
    124 
    125 static void _prop_dictionary_lock(void);
    126 static void _prop_dictionary_unlock(void);
    127 
    128 static const struct _prop_object_type _prop_object_type_dictionary = {
    129 	.pot_type		=	PROP_TYPE_DICTIONARY,
    130 	.pot_free		=	_prop_dictionary_free,
    131 	.pot_emergency_free	=	_prop_dictionary_emergency_free,
    132 	.pot_extern		=	_prop_dictionary_externalize,
    133 	.pot_equals		=	_prop_dictionary_equals,
    134 	.pot_equals_finish	=	_prop_dictionary_equals_finish,
    135 	.pot_lock 	        =       _prop_dictionary_lock,
    136 	.pot_unlock 	        =       _prop_dictionary_unlock,
    137 };
    138 
    139 static _prop_object_free_rv_t
    140 		_prop_dict_keysym_free(prop_stack_t, prop_object_t *);
    141 static bool	_prop_dict_keysym_externalize(
    142 				struct _prop_object_externalize_context *,
    143 				void *);
    144 static _prop_object_equals_rv_t
    145 		_prop_dict_keysym_equals(prop_object_t, prop_object_t,
    146 					 void **, void **,
    147 					 prop_object_t *, prop_object_t *);
    148 
    149 static const struct _prop_object_type _prop_object_type_dict_keysym = {
    150 	.pot_type	=	PROP_TYPE_DICT_KEYSYM,
    151 	.pot_free	=	_prop_dict_keysym_free,
    152 	.pot_extern	=	_prop_dict_keysym_externalize,
    153 	.pot_equals	=	_prop_dict_keysym_equals,
    154 };
    155 
    156 #define	prop_object_is_dictionary(x)		\
    157 	((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary)
    158 #define	prop_object_is_dictionary_keysym(x)	\
    159 	((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym)
    160 
    161 #define	prop_dictionary_is_immutable(x)		\
    162 				(((x)->pd_flags & PD_F_IMMUTABLE) != 0)
    163 
    164 struct _prop_dictionary_iterator {
    165 	struct _prop_object_iterator pdi_base;
    166 	unsigned int		pdi_index;
    167 };
    168 
    169 /*
    170  * Dictionary key symbols are immutable, and we are likely to have many
    171  * duplicated key symbols.  So, to save memory, we unique'ify key symbols
    172  * so we only have to have one copy of each string.
    173  */
    174 
    175 static int
    176 /*ARGSUSED*/
    177 _prop_dict_keysym_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED,
    178 				   const void *n1, const void *n2)
    179 {
    180 	const struct _prop_dictionary_keysym *pdk1 = n1;
    181 	const struct _prop_dictionary_keysym *pdk2 = n2;
    182 
    183 	return strcmp(pdk1->pdk_key, pdk2->pdk_key);
    184 }
    185 
    186 static int
    187 /*ARGSUSED*/
    188 _prop_dict_keysym_rb_compare_key(void *ctx _PROP_ARG_UNUSED,
    189 				 const void *n, const void *v)
    190 {
    191 	const struct _prop_dictionary_keysym *pdk = n;
    192 	const char *cp = v;
    193 
    194 	return strcmp(pdk->pdk_key, cp);
    195 }
    196 
    197 static const rb_tree_ops_t _prop_dict_keysym_rb_tree_ops = {
    198 	.rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes,
    199 	.rbto_compare_key = _prop_dict_keysym_rb_compare_key,
    200 	.rbto_node_offset = offsetof(struct _prop_dictionary_keysym, pdk_link),
    201 	.rbto_context = NULL
    202 };
    203 
    204 static struct rb_tree _prop_dict_keysym_tree;
    205 
    206 _PROP_ONCE_DECL(_prop_dict_init_once)
    207 _PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex)
    208 
    209 static int
    210 _prop_dict_init(void)
    211 {
    212 
    213 	_PROP_MUTEX_INIT(_prop_dict_keysym_tree_mutex);
    214 	rb_tree_init(&_prop_dict_keysym_tree,
    215 			   &_prop_dict_keysym_rb_tree_ops);
    216 	return 0;
    217 }
    218 
    219 static void
    220 _prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
    221 {
    222 
    223 	if (pdk->pdk_size <= PDK_SIZE_16)
    224 		_PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk);
    225 	else if (pdk->pdk_size <= PDK_SIZE_32)
    226 		_PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk);
    227 	else {
    228 		_PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128);
    229 		_PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk);
    230 	}
    231 }
    232 
    233 /* ARGSUSED */
    234 static _prop_object_free_rv_t
    235 _prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj)
    236 {
    237 	prop_dictionary_keysym_t pdk = *obj;
    238 
    239 	rb_tree_remove_node(&_prop_dict_keysym_tree, pdk);
    240 	_prop_dict_keysym_put(pdk);
    241 
    242 	return _PROP_OBJECT_FREE_DONE;
    243 }
    244 
    245 static bool
    246 _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
    247 			     void *v)
    248 {
    249 	prop_dictionary_keysym_t pdk = v;
    250 
    251 	/* We externalize these as strings, and they're never empty. */
    252 
    253 	_PROP_ASSERT(pdk->pdk_key[0] != '\0');
    254 
    255 	if (_prop_object_externalize_start_tag(ctx, "string") == false ||
    256 	    _prop_object_externalize_append_encoded_cstring(ctx,
    257 						pdk->pdk_key) == false ||
    258 	    _prop_object_externalize_end_tag(ctx, "string") == false)
    259 		return (false);
    260 
    261 	return (true);
    262 }
    263 
    264 /* ARGSUSED */
    265 static _prop_object_equals_rv_t
    266 _prop_dict_keysym_equals(prop_object_t v1, prop_object_t v2,
    267     void **stored_pointer1, void **stored_pointer2,
    268     prop_object_t *next_obj1, prop_object_t *next_obj2)
    269 {
    270 	prop_dictionary_keysym_t pdk1 = v1;
    271 	prop_dictionary_keysym_t pdk2 = v2;
    272 
    273 	/*
    274 	 * There is only ever one copy of a keysym at any given time,
    275 	 * so we can reduce this to a simple pointer equality check.
    276 	 */
    277 	if (pdk1 == pdk2)
    278 		return _PROP_OBJECT_EQUALS_TRUE;
    279 	else
    280 		return _PROP_OBJECT_EQUALS_FALSE;
    281 }
    282 
    283 static prop_dictionary_keysym_t
    284 _prop_dict_keysym_alloc(const char *key)
    285 {
    286 	prop_dictionary_keysym_t opdk, pdk, rpdk;
    287 	size_t size;
    288 
    289 	_PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init);
    290 
    291 	/*
    292 	 * Check to see if this already exists in the tree.  If it does,
    293 	 * we just retain it and return it.
    294 	 */
    295 	_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
    296 	opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key);
    297 	if (opdk != NULL) {
    298 		prop_object_retain(opdk);
    299 		_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
    300 		return (opdk);
    301 	}
    302 	_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
    303 
    304 	/*
    305 	 * Not in the tree.  Create it now.
    306 	 */
    307 
    308 	size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */;
    309 
    310 	if (size <= PDK_SIZE_16)
    311 		pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
    312 	else if (size <= PDK_SIZE_32)
    313 		pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
    314 	else if (size <= PDK_SIZE_128)
    315 		pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
    316 	else
    317 		pdk = NULL;	/* key too long */
    318 
    319 	if (pdk == NULL)
    320 		return (NULL);
    321 
    322 	_prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym);
    323 
    324 	strcpy(pdk->pdk_key, key);
    325 	pdk->pdk_size = size;
    326 
    327 	/*
    328 	 * We dropped the mutex when we allocated the new object, so
    329 	 * we have to check again if it is in the tree.
    330 	 */
    331 	_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
    332 	opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key);
    333 	if (opdk != NULL) {
    334 		prop_object_retain(opdk);
    335 		_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
    336 		_prop_dict_keysym_put(pdk);
    337 		return (opdk);
    338 	}
    339 	rpdk = rb_tree_insert_node(&_prop_dict_keysym_tree, pdk);
    340 	_PROP_ASSERT(rpdk == pdk);
    341 	_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
    342 	return (rpdk);
    343 }
    344 
    345 static _prop_object_free_rv_t
    346 _prop_dictionary_free(prop_stack_t stack, prop_object_t *obj)
    347 {
    348 	prop_dictionary_t pd = *obj;
    349 	prop_dictionary_keysym_t pdk;
    350 	prop_object_t po;
    351 
    352 	_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
    353 	_PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
    354 		     (pd->pd_capacity != 0 && pd->pd_array != NULL));
    355 
    356 	/* The empty dictorinary is easy, handle that first. */
    357 	if (pd->pd_count == 0) {
    358 		if (pd->pd_array != NULL)
    359 			_PROP_FREE(pd->pd_array, M_PROP_DICT);
    360 
    361 		_PROP_RWLOCK_DESTROY(pd->pd_rwlock);
    362 
    363 		_PROP_POOL_PUT(_prop_dictionary_pool, pd);
    364 
    365 		return (_PROP_OBJECT_FREE_DONE);
    366 	}
    367 
    368 	po = pd->pd_array[pd->pd_count - 1].pde_objref;
    369 	_PROP_ASSERT(po != NULL);
    370 
    371 	if (stack == NULL) {
    372 		/*
    373 		 * If we are in emergency release mode,
    374 		 * just let caller recurse down.
    375 		 */
    376 		*obj = po;
    377 		return (_PROP_OBJECT_FREE_FAILED);
    378 	}
    379 
    380 	/* Otherwise, try to push the current object on the stack. */
    381 	if (!_prop_stack_push(stack, pd, NULL, NULL, NULL)) {
    382 		/* Push failed, entering emergency release mode. */
    383 		return (_PROP_OBJECT_FREE_FAILED);
    384 	}
    385 	/* Object pushed on stack, caller will release it. */
    386 	--pd->pd_count;
    387 	pdk = pd->pd_array[pd->pd_count].pde_key;
    388 	_PROP_ASSERT(pdk != NULL);
    389 
    390 	prop_object_release(pdk);
    391 
    392 	*obj = po;
    393 	return (_PROP_OBJECT_FREE_RECURSE);
    394 }
    395 
    396 
    397 static void
    398 _prop_dictionary_lock(void)
    399 {
    400 
    401 	/* XXX: once necessary or paranoia? */
    402 	_PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init);
    403 	_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
    404 }
    405 
    406 static void
    407 _prop_dictionary_unlock(void)
    408 {
    409 	_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
    410 }
    411 
    412 static void
    413 _prop_dictionary_emergency_free(prop_object_t obj)
    414 {
    415 	prop_dictionary_t pd = obj;
    416 	prop_dictionary_keysym_t pdk;
    417 
    418 	_PROP_ASSERT(pd->pd_count != 0);
    419 	--pd->pd_count;
    420 
    421 	pdk = pd->pd_array[pd->pd_count].pde_key;
    422 	_PROP_ASSERT(pdk != NULL);
    423 	prop_object_release(pdk);
    424 }
    425 
    426 static bool
    427 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
    428 			     void *v)
    429 {
    430 	prop_dictionary_t pd = v;
    431 	prop_dictionary_keysym_t pdk;
    432 	struct _prop_object *po;
    433 	prop_object_iterator_t pi;
    434 	unsigned int i;
    435 	bool rv = false;
    436 
    437 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    438 
    439 	if (pd->pd_count == 0) {
    440 		_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    441 		return (_prop_object_externalize_empty_tag(ctx, "dict"));
    442 	}
    443 
    444 	if (_prop_object_externalize_start_tag(ctx, "dict") == false ||
    445 	    _prop_object_externalize_append_char(ctx, '\n') == false)
    446 		goto out;
    447 
    448 	pi = _prop_dictionary_iterator_locked(pd);
    449 	if (pi == NULL)
    450 		goto out;
    451 
    452 	ctx->poec_depth++;
    453 	_PROP_ASSERT(ctx->poec_depth != 0);
    454 
    455 	while ((pdk = _prop_dictionary_iterator_next_object_locked(pi))
    456 	    != NULL) {
    457 		po = _prop_dictionary_get_keysym(pd, pdk, true);
    458 		if (po == NULL ||
    459 		    _prop_object_externalize_start_tag(ctx, "key") == false ||
    460 		    _prop_object_externalize_append_encoded_cstring(ctx,
    461 						   pdk->pdk_key) == false ||
    462 		    _prop_object_externalize_end_tag(ctx, "key") == false ||
    463 		    (*po->po_type->pot_extern)(ctx, po) == false) {
    464 			prop_object_iterator_release(pi);
    465 			goto out;
    466 		}
    467 	}
    468 
    469 	prop_object_iterator_release(pi);
    470 
    471 	ctx->poec_depth--;
    472 	for (i = 0; i < ctx->poec_depth; i++) {
    473 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
    474 			goto out;
    475 	}
    476 	if (_prop_object_externalize_end_tag(ctx, "dict") == false)
    477 		goto out;
    478 
    479 	rv = true;
    480 
    481  out:
    482 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    483 	return (rv);
    484 }
    485 
    486 /* ARGSUSED */
    487 static _prop_object_equals_rv_t
    488 _prop_dictionary_equals(prop_object_t v1, prop_object_t v2,
    489     void **stored_pointer1, void **stored_pointer2,
    490     prop_object_t *next_obj1, prop_object_t *next_obj2)
    491 {
    492 	prop_dictionary_t dict1 = v1;
    493 	prop_dictionary_t dict2 = v2;
    494 	uintptr_t idx;
    495 	_prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE;
    496 
    497 	if (dict1 == dict2)
    498 		return (_PROP_OBJECT_EQUALS_TRUE);
    499 
    500 	_PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
    501 
    502 	idx = (uintptr_t)*stored_pointer1;
    503 
    504 	if (idx == 0) {
    505 		if ((uintptr_t)dict1 < (uintptr_t)dict2) {
    506 			_PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
    507 			_PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
    508 		} else {
    509 			_PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
    510 			_PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
    511 		}
    512 	}
    513 
    514 	if (dict1->pd_count != dict2->pd_count)
    515 		goto out;
    516 
    517 	if (idx == dict1->pd_count) {
    518 		rv = _PROP_OBJECT_EQUALS_TRUE;
    519 		goto out;
    520 	}
    521 
    522 	_PROP_ASSERT(idx < dict1->pd_count);
    523 
    524 	*stored_pointer1 = (void *)(idx + 1);
    525 	*stored_pointer2 = (void *)(idx + 1);
    526 
    527 	*next_obj1 = dict1->pd_array[idx].pde_objref;
    528 	*next_obj2 = dict2->pd_array[idx].pde_objref;
    529 
    530 	if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key,
    531 					   dict2->pd_array[idx].pde_key))
    532 		goto out;
    533 
    534 	return (_PROP_OBJECT_EQUALS_RECURSE);
    535 
    536  out:
    537  	_PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
    538 	_PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
    539 	return (rv);
    540 }
    541 
    542 static void
    543 _prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2)
    544 {
    545  	_PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock);
    546  	_PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock);
    547 }
    548 
    549 static prop_dictionary_t
    550 _prop_dictionary_alloc(unsigned int capacity)
    551 {
    552 	prop_dictionary_t pd;
    553 	struct _prop_dict_entry *array;
    554 
    555 	if (capacity != 0) {
    556 		array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
    557 		if (array == NULL)
    558 			return (NULL);
    559 	} else
    560 		array = NULL;
    561 
    562 	pd = _PROP_POOL_GET(_prop_dictionary_pool);
    563 	if (pd != NULL) {
    564 		_prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
    565 
    566 		_PROP_RWLOCK_INIT(pd->pd_rwlock);
    567 		pd->pd_array = array;
    568 		pd->pd_capacity = capacity;
    569 		pd->pd_count = 0;
    570 		pd->pd_flags = 0;
    571 
    572 		pd->pd_version = 0;
    573 	} else if (array != NULL)
    574 		_PROP_FREE(array, M_PROP_DICT);
    575 
    576 	return (pd);
    577 }
    578 
    579 static bool
    580 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
    581 {
    582 	struct _prop_dict_entry *array, *oarray;
    583 
    584 	/*
    585 	 * Dictionary must be WRITE-LOCKED.
    586 	 */
    587 
    588 	oarray = pd->pd_array;
    589 
    590 	array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
    591 	if (array == NULL)
    592 		return (false);
    593 	if (oarray != NULL)
    594 		memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
    595 	pd->pd_array = array;
    596 	pd->pd_capacity = capacity;
    597 
    598 	if (oarray != NULL)
    599 		_PROP_FREE(oarray, M_PROP_DICT);
    600 
    601 	return (true);
    602 }
    603 
    604 static prop_object_t
    605 _prop_dictionary_iterator_next_object_locked(void *v)
    606 {
    607 	struct _prop_dictionary_iterator *pdi = v;
    608 	prop_dictionary_t pd = pdi->pdi_base.pi_obj;
    609 	prop_dictionary_keysym_t pdk = NULL;
    610 
    611 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    612 
    613 	if (pd->pd_version != pdi->pdi_base.pi_version)
    614 		goto out;	/* dictionary changed during iteration */
    615 
    616 	_PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
    617 
    618 	if (pdi->pdi_index == pd->pd_count)
    619 		goto out;	/* we've iterated all objects */
    620 
    621 	pdk = pd->pd_array[pdi->pdi_index].pde_key;
    622 	pdi->pdi_index++;
    623 
    624  out:
    625 	return (pdk);
    626 }
    627 
    628 static prop_object_t
    629 _prop_dictionary_iterator_next_object(void *v)
    630 {
    631 	struct _prop_dictionary_iterator *pdi = v;
    632 	prop_dictionary_t pd _PROP_ARG_UNUSED = pdi->pdi_base.pi_obj;
    633 	prop_dictionary_keysym_t pdk;
    634 
    635 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    636 
    637 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    638 	pdk = _prop_dictionary_iterator_next_object_locked(pdi);
    639 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    640 	return (pdk);
    641 }
    642 
    643 static void
    644 _prop_dictionary_iterator_reset_locked(void *v)
    645 {
    646 	struct _prop_dictionary_iterator *pdi = v;
    647 	prop_dictionary_t pd = pdi->pdi_base.pi_obj;
    648 
    649 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    650 
    651 	pdi->pdi_index = 0;
    652 	pdi->pdi_base.pi_version = pd->pd_version;
    653 }
    654 
    655 static void
    656 _prop_dictionary_iterator_reset(void *v)
    657 {
    658 	struct _prop_dictionary_iterator *pdi = v;
    659 	prop_dictionary_t pd _PROP_ARG_UNUSED = pdi->pdi_base.pi_obj;
    660 
    661 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    662 	_prop_dictionary_iterator_reset_locked(pdi);
    663 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    664 }
    665 
    666 /*
    667  * prop_dictionary_create --
    668  *	Create a dictionary.
    669  */
    670 prop_dictionary_t
    671 prop_dictionary_create(void)
    672 {
    673 
    674 	return (_prop_dictionary_alloc(0));
    675 }
    676 
    677 /*
    678  * prop_dictionary_create_with_capacity --
    679  *	Create a dictionary with the capacity to store N objects.
    680  */
    681 prop_dictionary_t
    682 prop_dictionary_create_with_capacity(unsigned int capacity)
    683 {
    684 
    685 	return (_prop_dictionary_alloc(capacity));
    686 }
    687 
    688 /*
    689  * prop_dictionary_copy --
    690  *	Copy a dictionary.  The new dictionary has an initial capacity equal
    691  *	to the number of objects stored int the original dictionary.  The new
    692  *	dictionary contains refrences to the original dictionary's objects,
    693  *	not copies of those objects (i.e. a shallow copy).
    694  */
    695 prop_dictionary_t
    696 prop_dictionary_copy(prop_dictionary_t opd)
    697 {
    698 	prop_dictionary_t pd;
    699 	prop_dictionary_keysym_t pdk;
    700 	prop_object_t po;
    701 	unsigned int idx;
    702 
    703 	if (! prop_object_is_dictionary(opd))
    704 		return (NULL);
    705 
    706 	_PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
    707 
    708 	pd = _prop_dictionary_alloc(opd->pd_count);
    709 	if (pd != NULL) {
    710 		for (idx = 0; idx < opd->pd_count; idx++) {
    711 			pdk = opd->pd_array[idx].pde_key;
    712 			po = opd->pd_array[idx].pde_objref;
    713 
    714 			prop_object_retain(pdk);
    715 			prop_object_retain(po);
    716 
    717 			pd->pd_array[idx].pde_key = pdk;
    718 			pd->pd_array[idx].pde_objref = po;
    719 		}
    720 		pd->pd_count = opd->pd_count;
    721 		pd->pd_flags = opd->pd_flags;
    722 	}
    723 	_PROP_RWLOCK_UNLOCK(opd->pd_rwlock);
    724 	return (pd);
    725 }
    726 
    727 /*
    728  * prop_dictionary_copy_mutable --
    729  *	Like prop_dictionary_copy(), but the resulting dictionary is
    730  *	mutable.
    731  */
    732 prop_dictionary_t
    733 prop_dictionary_copy_mutable(prop_dictionary_t opd)
    734 {
    735 	prop_dictionary_t pd;
    736 
    737 	if (! prop_object_is_dictionary(opd))
    738 		return (NULL);
    739 
    740 	pd = prop_dictionary_copy(opd);
    741 	if (pd != NULL)
    742 		pd->pd_flags &= ~PD_F_IMMUTABLE;
    743 
    744 	return (pd);
    745 }
    746 
    747 /*
    748  * prop_dictionary_make_immutable --
    749  *	Set the immutable flag on that dictionary.
    750  */
    751 void
    752 prop_dictionary_make_immutable(prop_dictionary_t pd)
    753 {
    754 
    755 	_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
    756 	if (prop_dictionary_is_immutable(pd) == false)
    757 		pd->pd_flags |= PD_F_IMMUTABLE;
    758 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    759 }
    760 
    761 /*
    762  * prop_dictionary_count --
    763  *	Return the number of objects stored in the dictionary.
    764  */
    765 unsigned int
    766 prop_dictionary_count(prop_dictionary_t pd)
    767 {
    768 	unsigned int rv;
    769 
    770 	if (! prop_object_is_dictionary(pd))
    771 		return (0);
    772 
    773 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    774 	rv = pd->pd_count;
    775 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    776 
    777 	return (rv);
    778 }
    779 
    780 /*
    781  * prop_dictionary_ensure_capacity --
    782  *	Ensure that the dictionary has the capacity to store the specified
    783  *	total number of objects (including the objects already stored in
    784  *	the dictionary).
    785  */
    786 bool
    787 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
    788 {
    789 	bool rv;
    790 
    791 	if (! prop_object_is_dictionary(pd))
    792 		return (false);
    793 
    794 	_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
    795 	if (capacity > pd->pd_capacity)
    796 		rv = _prop_dictionary_expand(pd, capacity);
    797 	else
    798 		rv = true;
    799 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    800 	return (rv);
    801 }
    802 
    803 static prop_object_iterator_t
    804 _prop_dictionary_iterator_locked(prop_dictionary_t pd)
    805 {
    806 	struct _prop_dictionary_iterator *pdi;
    807 
    808 	if (! prop_object_is_dictionary(pd))
    809 		return (NULL);
    810 
    811 	pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
    812 	if (pdi == NULL)
    813 		return (NULL);
    814 	pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
    815 	pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
    816 	prop_object_retain(pd);
    817 	pdi->pdi_base.pi_obj = pd;
    818 	_prop_dictionary_iterator_reset_locked(pdi);
    819 
    820 	return (&pdi->pdi_base);
    821 }
    822 
    823 /*
    824  * prop_dictionary_iterator --
    825  *	Return an iterator for the dictionary.  The dictionary is retained by
    826  *	the iterator.
    827  */
    828 prop_object_iterator_t
    829 prop_dictionary_iterator(prop_dictionary_t pd)
    830 {
    831 	prop_object_iterator_t pi;
    832 
    833 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    834 	pi = _prop_dictionary_iterator_locked(pd);
    835 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    836 	return (pi);
    837 }
    838 
    839 /*
    840  * prop_dictionary_all_keys --
    841  *	Return an array containing a snapshot of all of the keys
    842  *	in the dictionary.
    843  */
    844 prop_array_t
    845 prop_dictionary_all_keys(prop_dictionary_t pd)
    846 {
    847 	prop_array_t array;
    848 	unsigned int idx;
    849 	bool rv = true;
    850 
    851 	if (! prop_object_is_dictionary(pd))
    852 		return (NULL);
    853 
    854 	/* There is no pressing need to lock the dictionary for this. */
    855 	array = prop_array_create_with_capacity(pd->pd_count);
    856 
    857 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    858 
    859 	for (idx = 0; idx < pd->pd_count; idx++) {
    860 		rv = prop_array_add(array, pd->pd_array[idx].pde_key);
    861 		if (rv == false)
    862 			break;
    863 	}
    864 
    865 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    866 
    867 	if (rv == false) {
    868 		prop_object_release(array);
    869 		array = NULL;
    870 	}
    871 	return (array);
    872 }
    873 
    874 static struct _prop_dict_entry *
    875 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
    876 		  unsigned int *idxp)
    877 {
    878 	struct _prop_dict_entry *pde;
    879 	unsigned int base, idx, distance;
    880 	int res;
    881 
    882 	/*
    883 	 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
    884 	 */
    885 
    886 	for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
    887 	     distance >>= 1) {
    888 		idx = base + (distance >> 1);
    889 		pde = &pd->pd_array[idx];
    890 		_PROP_ASSERT(pde->pde_key != NULL);
    891 		res = strcmp(key, pde->pde_key->pdk_key);
    892 		if (res == 0) {
    893 			if (idxp != NULL)
    894 				*idxp = idx;
    895 			return (pde);
    896 		}
    897 		if (res > 0) {	/* key > pdk_key: move right */
    898 			base = idx + 1;
    899 			distance--;
    900 		}		/* else move left */
    901 	}
    902 
    903 	/* idx points to the slot we looked at last. */
    904 	if (idxp != NULL)
    905 		*idxp = idx;
    906 	return (NULL);
    907 }
    908 
    909 static prop_object_t
    910 _prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked)
    911 {
    912 	const struct _prop_dict_entry *pde;
    913 	prop_object_t po = NULL;
    914 
    915 	if (! prop_object_is_dictionary(pd))
    916 		return (NULL);
    917 
    918 	if (!locked)
    919 		_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    920 	pde = _prop_dict_lookup(pd, key, NULL);
    921 	if (pde != NULL) {
    922 		_PROP_ASSERT(pde->pde_objref != NULL);
    923 		po = pde->pde_objref;
    924 	}
    925 	if (!locked)
    926 		_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    927 	return (po);
    928 }
    929 /*
    930  * prop_dictionary_get --
    931  *	Return the object stored with specified key.
    932  */
    933 prop_object_t
    934 prop_dictionary_get(prop_dictionary_t pd, const char *key)
    935 {
    936 	prop_object_t po = NULL;
    937 
    938 	if (! prop_object_is_dictionary(pd))
    939 		return (NULL);
    940 
    941 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
    942 	po = _prop_dictionary_get(pd, key, true);
    943 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
    944 	return (po);
    945 }
    946 
    947 static prop_object_t
    948 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
    949     bool locked)
    950 {
    951 
    952 	if (! (prop_object_is_dictionary(pd) &&
    953 	       prop_object_is_dictionary_keysym(pdk)))
    954 		return (NULL);
    955 
    956 	return (_prop_dictionary_get(pd, pdk->pdk_key, locked));
    957 }
    958 
    959 /*
    960  * prop_dictionary_get_keysym --
    961  *	Return the object stored at the location encoded by the keysym.
    962  */
    963 prop_object_t
    964 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
    965 {
    966 
    967 	return (_prop_dictionary_get_keysym(pd, pdk, false));
    968 }
    969 
    970 /*
    971  * prop_dictionary_set --
    972  *	Store a reference to an object at with the specified key.
    973  *	If the key already exisit, the original object is released.
    974  */
    975 bool
    976 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
    977 {
    978 	struct _prop_dict_entry *pde;
    979 	prop_dictionary_keysym_t pdk;
    980 	unsigned int idx;
    981 	bool rv = false;
    982 
    983 	if (! prop_object_is_dictionary(pd))
    984 		return (false);
    985 
    986 	_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
    987 
    988 	if (prop_dictionary_is_immutable(pd))
    989 		return (false);
    990 
    991 	_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
    992 
    993 	pde = _prop_dict_lookup(pd, key, &idx);
    994 	if (pde != NULL) {
    995 		prop_object_t opo = pde->pde_objref;
    996 		prop_object_retain(po);
    997 		pde->pde_objref = po;
    998 		prop_object_release(opo);
    999 		rv = true;
   1000 		goto out;
   1001 	}
   1002 
   1003 	pdk = _prop_dict_keysym_alloc(key);
   1004 	if (pdk == NULL)
   1005 		goto out;
   1006 
   1007 	if (pd->pd_count == pd->pd_capacity &&
   1008 	    _prop_dictionary_expand(pd,
   1009 	    			    pd->pd_capacity + EXPAND_STEP) == false) {
   1010 		prop_object_release(pdk);
   1011 	    	goto out;
   1012 	}
   1013 
   1014 	/* At this point, the store will succeed. */
   1015 	prop_object_retain(po);
   1016 
   1017 	if (pd->pd_count == 0) {
   1018 		pd->pd_array[0].pde_key = pdk;
   1019 		pd->pd_array[0].pde_objref = po;
   1020 		pd->pd_count++;
   1021 		pd->pd_version++;
   1022 		rv = true;
   1023 		goto out;
   1024 	}
   1025 
   1026 	pde = &pd->pd_array[idx];
   1027 	_PROP_ASSERT(pde->pde_key != NULL);
   1028 
   1029 	if (strcmp(key, pde->pde_key->pdk_key) < 0) {
   1030 		/*
   1031 		 * key < pdk_key: insert to the left.  This is the same as
   1032 		 * inserting to the right, except we decrement the current
   1033 		 * index first.
   1034 		 *
   1035 		 * Because we're unsigned, we have to special case 0
   1036 		 * (grumble).
   1037 		 */
   1038 		if (idx == 0) {
   1039 			memmove(&pd->pd_array[1], &pd->pd_array[0],
   1040 				pd->pd_count * sizeof(*pde));
   1041 			pd->pd_array[0].pde_key = pdk;
   1042 			pd->pd_array[0].pde_objref = po;
   1043 			pd->pd_count++;
   1044 			pd->pd_version++;
   1045 			rv = true;
   1046 			goto out;
   1047 		}
   1048 		idx--;
   1049 	}
   1050 
   1051 	memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
   1052 		(pd->pd_count - (idx + 1)) * sizeof(*pde));
   1053 	pd->pd_array[idx + 1].pde_key = pdk;
   1054 	pd->pd_array[idx + 1].pde_objref = po;
   1055 	pd->pd_count++;
   1056 
   1057 	pd->pd_version++;
   1058 
   1059 	rv = true;
   1060 
   1061  out:
   1062 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
   1063 	return (rv);
   1064 }
   1065 
   1066 /*
   1067  * prop_dictionary_set_keysym --
   1068  *	Replace the object in the dictionary at the location encoded by
   1069  *	the keysym.
   1070  */
   1071 bool
   1072 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
   1073 			   prop_object_t po)
   1074 {
   1075 
   1076 	if (! (prop_object_is_dictionary(pd) &&
   1077 	       prop_object_is_dictionary_keysym(pdk)))
   1078 		return (false);
   1079 
   1080 	return (prop_dictionary_set(pd, pdk->pdk_key, po));
   1081 }
   1082 
   1083 static void
   1084 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
   1085     unsigned int idx)
   1086 {
   1087 	prop_dictionary_keysym_t pdk = pde->pde_key;
   1088 	prop_object_t po = pde->pde_objref;
   1089 
   1090 	/*
   1091 	 * Dictionary must be WRITE-LOCKED.
   1092 	 */
   1093 
   1094 	_PROP_ASSERT(pd->pd_count != 0);
   1095 	_PROP_ASSERT(idx < pd->pd_count);
   1096 	_PROP_ASSERT(pde == &pd->pd_array[idx]);
   1097 
   1098 	idx++;
   1099 	memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
   1100 		(pd->pd_count - idx) * sizeof(*pde));
   1101 	pd->pd_count--;
   1102 	pd->pd_version++;
   1103 
   1104 
   1105 	prop_object_release(pdk);
   1106 
   1107 	prop_object_release(po);
   1108 }
   1109 
   1110 /*
   1111  * prop_dictionary_remove --
   1112  *	Remove the reference to an object with the specified key from
   1113  *	the dictionary.
   1114  */
   1115 void
   1116 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
   1117 {
   1118 	struct _prop_dict_entry *pde;
   1119 	unsigned int idx;
   1120 
   1121 	if (! prop_object_is_dictionary(pd))
   1122 		return;
   1123 
   1124 	_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
   1125 
   1126 	/* XXX Should this be a _PROP_ASSERT()? */
   1127 	if (prop_dictionary_is_immutable(pd))
   1128 		goto out;
   1129 
   1130 	pde = _prop_dict_lookup(pd, key, &idx);
   1131 	/* XXX Should this be a _PROP_ASSERT()? */
   1132 	if (pde == NULL)
   1133 		goto out;
   1134 
   1135 	_prop_dictionary_remove(pd, pde, idx);
   1136  out:
   1137 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
   1138 }
   1139 
   1140 /*
   1141  * prop_dictionary_remove_keysym --
   1142  *	Remove a reference to an object stored in the dictionary at the
   1143  *	location encoded by the keysym.
   1144  */
   1145 void
   1146 prop_dictionary_remove_keysym(prop_dictionary_t pd,
   1147 			      prop_dictionary_keysym_t pdk)
   1148 {
   1149 
   1150 	if (! (prop_object_is_dictionary(pd) &&
   1151 	       prop_object_is_dictionary_keysym(pdk)))
   1152 		return;
   1153 
   1154 	prop_dictionary_remove(pd, pdk->pdk_key);
   1155 }
   1156 
   1157 /*
   1158  * prop_dictionary_equals --
   1159  *	Return true if the two dictionaries are equivalent.  Note we do a
   1160  *	by-value comparison of the objects in the dictionary.
   1161  */
   1162 bool
   1163 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
   1164 {
   1165 	if (!prop_object_is_dictionary(dict1) ||
   1166 	    !prop_object_is_dictionary(dict2))
   1167 		return (false);
   1168 
   1169 	return (prop_object_equals(dict1, dict2));
   1170 }
   1171 
   1172 /*
   1173  * prop_dictionary_keysym_value --
   1174  *	Return a reference to the keysym's value.
   1175  */
   1176 const char *
   1177 prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk)
   1178 {
   1179 
   1180 	if (! prop_object_is_dictionary_keysym(pdk))
   1181 		return (NULL);
   1182 
   1183 	return (pdk->pdk_key);
   1184 }
   1185 
   1186 _PROP_DEPRECATED(prop_dictionary_keysym_cstring_nocopy,
   1187     "this program uses prop_dictionary_keysym_cstring_nocopy(), "
   1188     "which is deprecated; use prop_dictionary_keysym_value() instead.")
   1189 const char *
   1190 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
   1191 {
   1192 
   1193 	if (! prop_object_is_dictionary_keysym(pdk))
   1194 		return (NULL);
   1195 
   1196 	return (pdk->pdk_key);
   1197 }
   1198 
   1199 /*
   1200  * prop_dictionary_keysym_equals --
   1201  *	Return true if the two dictionary key symbols are equivalent.
   1202  *	Note: We do not compare the object references.
   1203  */
   1204 bool
   1205 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
   1206 			      prop_dictionary_keysym_t pdk2)
   1207 {
   1208 	if (!prop_object_is_dictionary_keysym(pdk1) ||
   1209 	    !prop_object_is_dictionary_keysym(pdk2))
   1210 		return (false);
   1211 
   1212 	return (prop_object_equals(pdk1, pdk2));
   1213 }
   1214 
   1215 /*
   1216  * prop_dictionary_externalize --
   1217  *	Externalize a dictionary, returning a NUL-terminated buffer
   1218  *	containing the XML-style representation.  The buffer is allocated
   1219  *	with the M_TEMP memory type.
   1220  */
   1221 char *
   1222 prop_dictionary_externalize(prop_dictionary_t pd)
   1223 {
   1224 	struct _prop_object_externalize_context *ctx;
   1225 	char *cp;
   1226 
   1227 	ctx = _prop_object_externalize_context_alloc();
   1228 	if (ctx == NULL)
   1229 		return (NULL);
   1230 
   1231 	if (_prop_object_externalize_header(ctx) == false ||
   1232 	    (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false ||
   1233 	    _prop_object_externalize_footer(ctx) == false) {
   1234 		/* We are responsible for releasing the buffer. */
   1235 		_PROP_FREE(ctx->poec_buf, M_TEMP);
   1236 		_prop_object_externalize_context_free(ctx);
   1237 		return (NULL);
   1238 	}
   1239 
   1240 	cp = ctx->poec_buf;
   1241 	_prop_object_externalize_context_free(ctx);
   1242 
   1243 	return (cp);
   1244 }
   1245 
   1246 /*
   1247  * _prop_dictionary_internalize --
   1248  *	Parse a <dict>...</dict> and return the object created from the
   1249  *	external representation.
   1250  *
   1251  * Internal state in via rec_data is the storage area for the last processed
   1252  * key.
   1253  * _prop_dictionary_internalize_body is the upper half of the parse loop.
   1254  * It is responsible for parsing the key directly and storing it in the area
   1255  * referenced by rec_data.
   1256  * _prop_dictionary_internalize_cont is the lower half and called with the value
   1257  * associated with the key.
   1258  */
   1259 static bool _prop_dictionary_internalize_body(prop_stack_t,
   1260     prop_object_t *, struct _prop_object_internalize_context *, char *);
   1261 
   1262 bool
   1263 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
   1264     struct _prop_object_internalize_context *ctx)
   1265 {
   1266 	prop_dictionary_t dict;
   1267 	char *tmpkey;
   1268 
   1269 	/* We don't currently understand any attributes. */
   1270 	if (ctx->poic_tagattr != NULL)
   1271 		return (true);
   1272 
   1273 	dict = prop_dictionary_create();
   1274 	if (dict == NULL)
   1275 		return (true);
   1276 
   1277 	if (ctx->poic_is_empty_element) {
   1278 		*obj = dict;
   1279 		return (true);
   1280 	}
   1281 
   1282 	tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
   1283 	if (tmpkey == NULL) {
   1284 		prop_object_release(dict);
   1285 		return (true);
   1286 	}
   1287 
   1288 	*obj = dict;
   1289 	/*
   1290 	 * Opening tag is found, storage for key allocated and
   1291 	 * now continue to the first element.
   1292 	 */
   1293 	return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
   1294 }
   1295 
   1296 static bool
   1297 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
   1298     struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
   1299 {
   1300 	prop_dictionary_t dict = *obj;
   1301 	char *tmpkey = data;
   1302 
   1303 	_PROP_ASSERT(tmpkey != NULL);
   1304 
   1305 	if (child == NULL ||
   1306 	    prop_dictionary_set(dict, tmpkey, child) == false) {
   1307 		_PROP_FREE(tmpkey, M_TEMP);
   1308 		if (child != NULL)
   1309 			prop_object_release(child);
   1310 		prop_object_release(dict);
   1311 		*obj = NULL;
   1312 		return (true);
   1313 	}
   1314 
   1315 	prop_object_release(child);
   1316 
   1317 	/*
   1318 	 * key, value was added, now continue looking for the next key
   1319 	 * or the closing tag.
   1320 	 */
   1321 	return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
   1322 }
   1323 
   1324 static bool
   1325 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
   1326     struct _prop_object_internalize_context *ctx, char *tmpkey)
   1327 {
   1328 	prop_dictionary_t dict = *obj;
   1329 	size_t keylen;
   1330 
   1331 	/* Fetch the next tag. */
   1332 	if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
   1333 		goto bad;
   1334 
   1335 	/* Check to see if this is the end of the dictionary. */
   1336 	if (_PROP_TAG_MATCH(ctx, "dict") &&
   1337 	    ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
   1338 		_PROP_FREE(tmpkey, M_TEMP);
   1339 		return (true);
   1340 	}
   1341 
   1342 	/* Ok, it must be a non-empty key start tag. */
   1343 	if (!_PROP_TAG_MATCH(ctx, "key") ||
   1344 	    ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
   1345 	    ctx->poic_is_empty_element)
   1346 	    	goto bad;
   1347 
   1348 	if (_prop_object_internalize_decode_string(ctx,
   1349 					tmpkey, PDK_MAXKEY, &keylen,
   1350 					&ctx->poic_cp) == false)
   1351 		goto bad;
   1352 
   1353 	_PROP_ASSERT(keylen <= PDK_MAXKEY);
   1354 	tmpkey[keylen] = '\0';
   1355 
   1356 	if (_prop_object_internalize_find_tag(ctx, "key",
   1357 				_PROP_TAG_TYPE_END) == false)
   1358 		goto bad;
   1359 
   1360 	/* ..and now the beginning of the value. */
   1361 	if (_prop_object_internalize_find_tag(ctx, NULL,
   1362 				_PROP_TAG_TYPE_START) == false)
   1363 		goto bad;
   1364 
   1365 	/*
   1366 	 * Key is found, now wait for value to be parsed.
   1367 	 */
   1368 	if (_prop_stack_push(stack, *obj,
   1369 			     _prop_dictionary_internalize_continue,
   1370 			     tmpkey, NULL))
   1371 		return (false);
   1372 
   1373  bad:
   1374 	_PROP_FREE(tmpkey, M_TEMP);
   1375 	prop_object_release(dict);
   1376 	*obj = NULL;
   1377 	return (true);
   1378 }
   1379 
   1380 /*
   1381  * prop_dictionary_internalize --
   1382  *	Create a dictionary by parsing the NUL-terminated XML-style
   1383  *	representation.
   1384  */
   1385 prop_dictionary_t
   1386 prop_dictionary_internalize(const char *xml)
   1387 {
   1388 	return _prop_generic_internalize(xml, "dict");
   1389 }
   1390 
   1391 #if !defined(_KERNEL) && !defined(_STANDALONE)
   1392 /*
   1393  * prop_dictionary_externalize_to_file --
   1394  *	Externalize a dictionary to the specified file.
   1395  */
   1396 bool
   1397 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
   1398 {
   1399 	char *xml;
   1400 	bool rv;
   1401 	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
   1402 
   1403 	xml = prop_dictionary_externalize(dict);
   1404 	if (xml == NULL)
   1405 		return (false);
   1406 	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
   1407 	if (rv == false)
   1408 		save_errno = errno;
   1409 	_PROP_FREE(xml, M_TEMP);
   1410 	if (rv == false)
   1411 		errno = save_errno;
   1412 
   1413 	return (rv);
   1414 }
   1415 
   1416 /*
   1417  * prop_dictionary_internalize_from_file --
   1418  *	Internalize a dictionary from a file.
   1419  */
   1420 prop_dictionary_t
   1421 prop_dictionary_internalize_from_file(const char *fname)
   1422 {
   1423 	struct _prop_object_internalize_mapped_file *mf;
   1424 	prop_dictionary_t dict;
   1425 
   1426 	mf = _prop_object_internalize_map_file(fname);
   1427 	if (mf == NULL)
   1428 		return (NULL);
   1429 	dict = prop_dictionary_internalize(mf->poimf_xml);
   1430 	_prop_object_internalize_unmap_file(mf);
   1431 
   1432 	return (dict);
   1433 }
   1434 #endif /* !_KERNEL && !_STANDALONE */
   1435