Home | History | Annotate | Line # | Download | only in libprop
prop_dictionary.c revision 1.3
      1 /*	$NetBSD: prop_dictionary.c,v 1.3 2006/05/18 03:05:19 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 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  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *      This product includes software developed by the NetBSD
     21  *      Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <prop/prop_dictionary.h>
     40 #include <prop/prop_string.h>
     41 #include "prop_object_impl.h"
     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		pde_obj;
     64 	prop_object_t			pde_objref;
     65 	size_t				pde_size;
     66 	char 				pde_key[1];
     67 	/* actually variable length */
     68 };
     69 
     70 	/* pde_key[1] takes care of the NUL */
     71 #define	PDE_SIZE_16		(sizeof(struct _prop_dictionary_keysym) + 16)
     72 #define	PDE_SIZE_32		(sizeof(struct _prop_dictionary_keysym) + 32)
     73 #define	PDE_SIZE_128		(sizeof(struct _prop_dictionary_keysym) + 128)
     74 
     75 #define	PDE_MAXKEY		128
     76 
     77 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDE_SIZE_16, "pdict16")
     78 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDE_SIZE_32, "pdict32")
     79 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDE_SIZE_128, "pdict128")
     80 
     81 struct _prop_dictionary {
     82 	struct _prop_object	pd_obj;
     83 	prop_dictionary_keysym_t *pd_array;
     84 	unsigned int		pd_capacity;
     85 	unsigned int		pd_count;
     86 	int			pd_flags;
     87 
     88 	uint32_t		pd_version;
     89 };
     90 
     91 #define	PD_F_IMMUTABLE		0x01	/* dictionary is immutable */
     92 
     93 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
     94 		"propdict")
     95 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
     96 		    "property dictionary container object")
     97 
     98 static void		_prop_dictionary_free(void *);
     99 static boolean_t	_prop_dictionary_externalize(
    100 				struct _prop_object_externalize_context *,
    101 				void *);
    102 static boolean_t	_prop_dictionary_equals(void *, void *);
    103 
    104 static const struct _prop_object_type _prop_object_type_dictionary = {
    105 	.pot_type	=	PROP_TYPE_DICTIONARY,
    106 	.pot_free	=	_prop_dictionary_free,
    107 	.pot_extern	=	_prop_dictionary_externalize,
    108 	.pot_equals	=	_prop_dictionary_equals,
    109 };
    110 
    111 static void		_prop_dict_entry_free(void *);
    112 static boolean_t	_prop_dict_entry_externalize(
    113 				struct _prop_object_externalize_context *,
    114 				void *);
    115 static boolean_t	_prop_dict_entry_equals(void *, void *);
    116 
    117 static const struct _prop_object_type _prop_object_type_dict_keysym = {
    118 	.pot_type	=	PROP_TYPE_DICT_KEYSYM,
    119 	.pot_free	=	_prop_dict_entry_free,
    120 	.pot_extern	=	_prop_dict_entry_externalize,
    121 	.pot_equals	=	_prop_dict_entry_equals,
    122 };
    123 
    124 #define	prop_object_is_dictionary(x)		\
    125 		((x)->pd_obj.po_type == &_prop_object_type_dictionary)
    126 #define	prop_object_is_dictionary_keysym(x)	\
    127 		((x)->pde_obj.po_type == &_prop_object_type_dict_keysym)
    128 
    129 #define	prop_dictionary_is_immutable(x)		\
    130 				(((x)->pd_flags & PD_F_IMMUTABLE) != 0)
    131 
    132 struct _prop_dictionary_iterator {
    133 	struct _prop_object_iterator pdi_base;
    134 	unsigned int		pdi_index;
    135 };
    136 
    137 static void
    138 _prop_dict_entry_free(void *v)
    139 {
    140 	prop_dictionary_keysym_t pde = v;
    141 	prop_object_t po;
    142 
    143 	_PROP_ASSERT(pde->pde_objref != NULL);
    144 	po = pde->pde_objref;
    145 
    146 	if (pde->pde_size <= PDE_SIZE_16)
    147 		_PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pde);
    148 	else if (pde->pde_size <= PDE_SIZE_32)
    149 		_PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pde);
    150 	else {
    151 		_PROP_ASSERT(pde->pde_size <= PDE_SIZE_128);
    152 		_PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pde);
    153 	}
    154 
    155 	prop_object_release(po);
    156 }
    157 
    158 static boolean_t
    159 _prop_dict_entry_externalize(struct _prop_object_externalize_context *ctx,
    160 			     void *v)
    161 {
    162 	prop_dictionary_keysym_t pde = v;
    163 
    164 	/* We externalize these as strings, and they're never empty. */
    165 
    166 	_PROP_ASSERT(pde->pde_key[0] != '\0');
    167 
    168 	if (_prop_object_externalize_start_tag(ctx, "string") == FALSE ||
    169 	    _prop_object_externalize_append_encoded_cstring(ctx,
    170 						pde->pde_key) == FALSE ||
    171 	    _prop_object_externalize_end_tag(ctx, "string") == FALSE)
    172 		return (FALSE);
    173 
    174 	return (TRUE);
    175 }
    176 
    177 static boolean_t
    178 _prop_dict_entry_equals(void *v1, void *v2)
    179 {
    180 	prop_dictionary_keysym_t pde1 = v1;
    181 	prop_dictionary_keysym_t pde2 = v2;
    182 
    183 	_PROP_ASSERT(prop_object_is_dictionary_keysym(pde1));
    184 	_PROP_ASSERT(prop_object_is_dictionary_keysym(pde2));
    185 	if (pde1 == pde2)
    186 		return (TRUE);
    187 	return (strcmp(pde1->pde_key, pde2->pde_key) == 0);
    188 }
    189 
    190 static prop_dictionary_keysym_t
    191 _prop_dict_entry_alloc(const char *key, prop_object_t obj)
    192 {
    193 	prop_dictionary_keysym_t pde;
    194 	size_t size;
    195 
    196 	size = sizeof(*pde) + strlen(key) /* pde_key[1] covers the NUL */;
    197 
    198 	if (size <= PDE_SIZE_16)
    199 		pde = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
    200 	else if (size <= PDE_SIZE_32)
    201 		pde = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
    202 	else if (size <= PDE_SIZE_128)
    203 		pde = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
    204 	else
    205 		return (NULL);	/* key too long */
    206 
    207 	if (pde != NULL) {
    208 		_prop_object_init(&pde->pde_obj,
    209 				  &_prop_object_type_dict_keysym);
    210 
    211 		strcpy(pde->pde_key, key);
    212 
    213 		prop_object_retain(obj);
    214 		pde->pde_objref = obj;
    215 		pde->pde_size = size;
    216 	}
    217 	return (pde);
    218 }
    219 
    220 static void
    221 _prop_dictionary_free(void *v)
    222 {
    223 	prop_dictionary_t pd = v;
    224 	prop_dictionary_keysym_t pde;
    225 	unsigned int idx;
    226 
    227 	_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
    228 	_PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
    229 		     (pd->pd_capacity != 0 && pd->pd_array != NULL));
    230 
    231 	for (idx = 0; idx < pd->pd_count; idx++) {
    232 		pde = pd->pd_array[idx];
    233 		_PROP_ASSERT(pde != NULL);
    234 		prop_object_release(pde);
    235 	}
    236 
    237 	if (pd->pd_array != NULL)
    238 		_PROP_FREE(pd->pd_array, M_PROP_DICT);
    239 
    240 	_PROP_POOL_PUT(_prop_dictionary_pool, pd);
    241 }
    242 
    243 static boolean_t
    244 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
    245 			     void *v)
    246 {
    247 	prop_dictionary_t pd = v;
    248 	prop_dictionary_keysym_t pde;
    249 	struct _prop_object *po;
    250 	prop_object_iterator_t pi;
    251 	unsigned int i;
    252 
    253 	if (pd->pd_count == 0)
    254 		return (_prop_object_externalize_empty_tag(ctx, "dict"));
    255 
    256 	/* XXXJRT Hint "count" for the internalize step? */
    257 	if (_prop_object_externalize_start_tag(ctx, "dict") == FALSE ||
    258 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
    259 		return (FALSE);
    260 
    261 	pi = prop_dictionary_iterator(pd);
    262 	if (pi == NULL)
    263 		return (FALSE);
    264 
    265 	ctx->poec_depth++;
    266 	_PROP_ASSERT(ctx->poec_depth != 0);
    267 
    268 	while ((pde = prop_object_iterator_next(pi)) != NULL) {
    269 		po = prop_dictionary_get_keysym(pd, pde);
    270 		if (po == NULL ||
    271 		    _prop_object_externalize_start_tag(ctx, "key") == FALSE ||
    272 		    _prop_object_externalize_append_encoded_cstring(ctx,
    273 						   pde->pde_key) == FALSE ||
    274 		    _prop_object_externalize_end_tag(ctx, "key") == FALSE ||
    275 		    (*po->po_type->pot_extern)(ctx, po) == FALSE) {
    276 			prop_object_iterator_release(pi);
    277 			return (FALSE);
    278 		}
    279 	}
    280 
    281 	prop_object_iterator_release(pi);
    282 
    283 	ctx->poec_depth--;
    284 	for (i = 0; i < ctx->poec_depth; i++) {
    285 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
    286 			return (FALSE);
    287 	}
    288 	if (_prop_object_externalize_end_tag(ctx, "dict") == FALSE)
    289 		return (FALSE);
    290 
    291 	return (TRUE);
    292 }
    293 
    294 static boolean_t
    295 _prop_dictionary_equals(void *v1, void *v2)
    296 {
    297 	prop_dictionary_t dict1 = v1;
    298 	prop_dictionary_t dict2 = v2;
    299 	prop_dictionary_keysym_t pde1, pde2;
    300 	unsigned int idx;
    301 
    302 	_PROP_ASSERT(prop_object_is_dictionary(dict1));
    303 	_PROP_ASSERT(prop_object_is_dictionary(dict2));
    304 	if (dict1 == dict2)
    305 		return (TRUE);
    306 	if (dict1->pd_count != dict2->pd_count)
    307 		return (FALSE);
    308 
    309 	for (idx = 0; idx < dict1->pd_count; idx++) {
    310 		pde1 = dict1->pd_array[idx];
    311 		pde2 = dict2->pd_array[idx];
    312 
    313 		if (prop_dictionary_keysym_equals(pde1, pde2) == FALSE)
    314 			return (FALSE);
    315 		if (prop_object_equals(pde1->pde_objref,
    316 				       pde2->pde_objref) == FALSE)
    317 			return (FALSE);
    318 	}
    319 
    320 	return (TRUE);
    321 }
    322 
    323 static prop_dictionary_t
    324 _prop_dictionary_alloc(unsigned int capacity)
    325 {
    326 	prop_dictionary_t pd;
    327 	prop_dictionary_keysym_t *array;
    328 
    329 	if (capacity != 0) {
    330 		array = _PROP_CALLOC(capacity *
    331 					sizeof(prop_dictionary_keysym_t),
    332 				     M_PROP_DICT);
    333 		if (array == NULL)
    334 			return (NULL);
    335 	} else
    336 		array = NULL;
    337 
    338 	pd = _PROP_POOL_GET(_prop_dictionary_pool);
    339 	if (pd != NULL) {
    340 		_prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
    341 
    342 		pd->pd_array = array;
    343 		pd->pd_capacity = capacity;
    344 		pd->pd_count = 0;
    345 		pd->pd_flags = 0;
    346 
    347 		pd->pd_version = 0;
    348 	} else if (array != NULL)
    349 		_PROP_FREE(array, M_PROP_DICT);
    350 
    351 	return (pd);
    352 }
    353 
    354 static boolean_t
    355 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
    356 {
    357 	prop_dictionary_keysym_t *array, *oarray;
    358 
    359 	oarray = pd->pd_array;
    360 
    361 	array = _PROP_CALLOC(capacity * sizeof(prop_dictionary_keysym_t),
    362 			     M_PROP_DICT);
    363 	if (array == NULL)
    364 		return (FALSE);
    365 	if (oarray != NULL)
    366 		memcpy(array, oarray,
    367 		       pd->pd_capacity * sizeof(prop_dictionary_keysym_t));
    368 	pd->pd_array = array;
    369 	pd->pd_capacity = capacity;
    370 
    371 	if (oarray != NULL)
    372 		_PROP_FREE(oarray, M_PROP_DICT);
    373 
    374 	return (TRUE);
    375 }
    376 
    377 static prop_object_t
    378 _prop_dictionary_iterator_next_object(void *v)
    379 {
    380 	struct _prop_dictionary_iterator *pdi = v;
    381 	prop_dictionary_t pd = pdi->pdi_base.pi_obj;
    382 	prop_dictionary_keysym_t pde;
    383 
    384 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    385 
    386 	if (pd->pd_version != pdi->pdi_base.pi_version)
    387 		return (NULL);	/* dictionary changed during iteration */
    388 
    389 	_PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
    390 
    391 	if (pdi->pdi_index == pd->pd_count)
    392 		return (NULL);	/* we've iterated all objects */
    393 
    394 	pde = pd->pd_array[pdi->pdi_index];
    395 	pdi->pdi_index++;
    396 
    397 	return (pde);
    398 }
    399 
    400 static void
    401 _prop_dictionary_iterator_reset(void *v)
    402 {
    403 	struct _prop_dictionary_iterator *pdi = v;
    404 	prop_dictionary_t pd = pdi->pdi_base.pi_obj;
    405 
    406 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    407 
    408 	pdi->pdi_index = 0;
    409 	pdi->pdi_base.pi_version = pd->pd_version;
    410 }
    411 
    412 /*
    413  * prop_dictionary_create --
    414  *	Create a dictionary.
    415  */
    416 prop_dictionary_t
    417 prop_dictionary_create(void)
    418 {
    419 
    420 	return (_prop_dictionary_alloc(0));
    421 }
    422 
    423 /*
    424  * prop_dictionary_create_with_capacity --
    425  *	Create a dictionary with the capacity to store N objects.
    426  */
    427 prop_dictionary_t
    428 prop_dictionary_create_with_capacity(unsigned int capacity)
    429 {
    430 
    431 	return (_prop_dictionary_alloc(capacity));
    432 }
    433 
    434 /*
    435  * prop_dictionary_copy --
    436  *	Copy a dictionary.  The new dictionary contains refrences to
    437  *	the original dictionary's objects, not copies of those objects
    438  *	(i.e. a shallow copy).
    439  */
    440 prop_dictionary_t
    441 prop_dictionary_copy(prop_dictionary_t opd)
    442 {
    443 	prop_dictionary_t pd;
    444 	prop_dictionary_keysym_t pde;
    445 	unsigned int idx;
    446 
    447 	_PROP_ASSERT(prop_object_is_dictionary(opd));
    448 
    449 	if (prop_dictionary_is_immutable(opd) == FALSE)
    450 		return (prop_dictionary_copy_mutable(opd));
    451 
    452 	/*
    453 	 * Copies of immutable dictionaries refrence the same
    454 	 * dictionary entry objects to save space.
    455 	 */
    456 
    457 	pd = _prop_dictionary_alloc(opd->pd_count);
    458 	if (pd != NULL) {
    459 		for (idx = 0; idx < opd->pd_count; idx++) {
    460 			pde = opd->pd_array[idx];
    461 			prop_object_retain(pde);
    462 			pd->pd_array[idx] = pde;
    463 		}
    464 		pd->pd_count = opd->pd_count;
    465 		pd->pd_flags = opd->pd_flags;
    466 	}
    467 	return (pd);
    468 }
    469 
    470 /*
    471  * prop_dictionary_copy_mutable --
    472  *	Like prop_dictionary_copy(), but the resulting dictionary is
    473  *	mutable.
    474  */
    475 prop_dictionary_t
    476 prop_dictionary_copy_mutable(prop_dictionary_t opd)
    477 {
    478 	prop_dictionary_t pd;
    479 	prop_dictionary_keysym_t opde, pde;
    480 	unsigned int idx;
    481 
    482 	_PROP_ASSERT(prop_object_is_dictionary(opd));
    483 
    484 	pd = _prop_dictionary_alloc(opd->pd_count);
    485 	if (pd != NULL) {
    486 		for (idx = 0; idx > opd->pd_count; idx++) {
    487 			opde = opd->pd_array[idx];
    488 			pde = _prop_dict_entry_alloc(opde->pde_key,
    489 						     opde->pde_objref);
    490 			if (pde == NULL) {
    491 				prop_object_release(pd);
    492 				return (NULL);
    493 			}
    494 			pd->pd_array[idx] = pde;
    495 			pd->pd_count++;
    496 		}
    497 	}
    498 	return (pd);
    499 }
    500 
    501 /*
    502  * prop_dictionary_count --
    503  *	Return the number of objects stored in the dictionary.
    504  */
    505 unsigned int
    506 prop_dictionary_count(prop_dictionary_t pd)
    507 {
    508 
    509 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    510 	return (pd->pd_count);
    511 }
    512 
    513 /*
    514  * prop_dictionary_iterator --
    515  *	Return an iterator for the dictionary.  The dictionary is retained by
    516  *	the iterator.
    517  */
    518 prop_object_iterator_t
    519 prop_dictionary_iterator(prop_dictionary_t pd)
    520 {
    521 	struct _prop_dictionary_iterator *pdi;
    522 
    523 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    524 
    525 	pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
    526 	if (pdi == NULL)
    527 		return (NULL);
    528 	pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
    529 	pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
    530 	prop_object_retain(pd);
    531 	pdi->pdi_base.pi_obj = pd;
    532 	pdi->pdi_base.pi_version = pd->pd_version;
    533 
    534 	_prop_dictionary_iterator_reset(pdi);
    535 
    536 	return (&pdi->pdi_base);
    537 }
    538 
    539 static prop_dictionary_keysym_t
    540 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
    541 		  unsigned int *idxp)
    542 {
    543 	prop_dictionary_keysym_t pde;
    544 	unsigned int base, idx, distance;
    545 	int res;
    546 
    547 	for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
    548 	     distance >>= 1) {
    549 		idx = base + (distance >> 1);
    550 		pde = pd->pd_array[idx];
    551 		_PROP_ASSERT(pde != NULL);
    552 		res = strcmp(key, pde->pde_key);
    553 		if (res == 0) {
    554 			if (idxp != NULL)
    555 				*idxp = idx;
    556 			return (pde);
    557 		}
    558 		if (res > 0) {	/* key > pde->pde_key: move right */
    559 			base = idx + 1;
    560 			distance--;
    561 		}		/* else move left */
    562 	}
    563 
    564 	/* idx points to the slot we looked at last. */
    565 	if (idxp != NULL)
    566 		*idxp = idx;
    567 	return (NULL);
    568 }
    569 
    570 /*
    571  * prop_dictionary_get --
    572  *	Return the object stored with specified key.
    573  */
    574 prop_object_t
    575 prop_dictionary_get(prop_dictionary_t pd, const char *key)
    576 {
    577 	prop_dictionary_keysym_t pde;
    578 
    579 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    580 
    581 	pde = _prop_dict_lookup(pd, key, NULL);
    582 	if (pde != NULL) {
    583 		_PROP_ASSERT(pde->pde_objref != NULL);
    584 		return (pde->pde_objref);
    585 	}
    586 	return (NULL);
    587 }
    588 
    589 /*
    590  * prop_dictionary_get_keysym --
    591  *	Return the object stored at the location encoded by the keysym.
    592  */
    593 prop_object_t
    594 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pde)
    595 {
    596 
    597 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    598 	_PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
    599 	_PROP_ASSERT(pde->pde_objref != NULL);
    600 
    601 	return (pde->pde_objref);
    602 }
    603 
    604 /*
    605  * prop_dictionary_set --
    606  *	Store a reference to an object at with the specified key.
    607  *	If the key already exisit, the original object is released.
    608  */
    609 boolean_t
    610 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
    611 {
    612 	prop_dictionary_keysym_t pde, opde;
    613 	unsigned int idx;
    614 
    615 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    616 	_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
    617 
    618 	if (prop_dictionary_is_immutable(pd))
    619 		return (FALSE);
    620 
    621 	pde = _prop_dict_lookup(pd, key, &idx);
    622 	if (pde != NULL) {
    623 		prop_object_t opo = pde->pde_objref;
    624 		prop_object_retain(po);
    625 		pde->pde_objref = po;
    626 		prop_object_release(opo);
    627 		return (TRUE);
    628 	}
    629 
    630 	pde = _prop_dict_entry_alloc(key, po);
    631 	if (pde == NULL)
    632 		return (FALSE);
    633 
    634 	if (pd->pd_count == pd->pd_capacity &&
    635 	    _prop_dictionary_expand(pd, EXPAND_STEP) == FALSE) {
    636 		_prop_dict_entry_free(pde);
    637 	    	return (FALSE);
    638 	}
    639 
    640 	if (pd->pd_count == 0) {
    641 		pd->pd_array[0] = pde;
    642 		pd->pd_count++;
    643 		pd->pd_version++;
    644 		return (TRUE);
    645 	}
    646 
    647 	opde = pd->pd_array[idx];
    648 	_PROP_ASSERT(opde != NULL);
    649 
    650 	if (strcmp(key, opde->pde_key) < 0) {
    651 		/*
    652 		 * key < opde->pde_key: insert to the left.  This is
    653 		 * the same as inserting to the right, except we decrement
    654 		 * the current index first.
    655 		 *
    656 		 * Because we're unsigned, we have to special case 0
    657 		 * (grumble).
    658 		 */
    659 		if (idx == 0) {
    660 			memmove(&pd->pd_array[1], &pd->pd_array[0],
    661 				pd->pd_count * sizeof(*pde));
    662 			pd->pd_array[0] = pde;
    663 			pd->pd_count++;
    664 			pd->pd_version++;
    665 			return (TRUE);
    666 		}
    667 		idx--;
    668 	}
    669 
    670 	memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
    671 		(pd->pd_count - (idx + 1)) * sizeof(*pde));
    672 	pd->pd_array[idx + 1] = pde;
    673 	pd->pd_count++;
    674 
    675 	pd->pd_version++;
    676 
    677 	return (TRUE);
    678 }
    679 
    680 /*
    681  * prop_dictionary_set_keysym --
    682  *	Replace the object in the dictionary at the location encoded by
    683  *	the keysym.
    684  */
    685 boolean_t
    686 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pde,
    687 			   prop_object_t po)
    688 {
    689 	prop_object_t opo;
    690 
    691 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    692 	_PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
    693 	_PROP_ASSERT(pde->pde_objref != NULL);
    694 
    695 	if (prop_dictionary_is_immutable(pd))
    696 		return (FALSE);
    697 
    698 	opo = pde->pde_objref;
    699 	prop_object_retain(po);
    700 	pde->pde_objref = po;
    701 	prop_object_release(opo);
    702 	return (TRUE);
    703 }
    704 
    705 static void
    706 _prop_dictionary_remove(prop_dictionary_t pd, prop_dictionary_keysym_t pde,
    707     unsigned int idx)
    708 {
    709 
    710 	_PROP_ASSERT(pd->pd_count != 0);
    711 	_PROP_ASSERT(idx < pd->pd_count);
    712 	_PROP_ASSERT(pd->pd_array[idx] == pde);
    713 
    714 	idx++;
    715 	memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
    716 		(pd->pd_count - idx) * sizeof(*pde));
    717 	pd->pd_count--;
    718 	pd->pd_version++;
    719 
    720 	prop_object_release(pde);
    721 }
    722 
    723 /*
    724  * prop_dictionary_remove --
    725  *	Remove the reference to an object with the specified key from
    726  *	the dictionary.
    727  */
    728 void
    729 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
    730 {
    731 	prop_dictionary_keysym_t pde;
    732 	unsigned int idx;
    733 
    734 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    735 
    736 	/* XXX Should this be a _PROP_ASSERT()? */
    737 	if (prop_dictionary_is_immutable(pd))
    738 		return;
    739 
    740 	pde = _prop_dict_lookup(pd, key, &idx);
    741 	/* XXX Should this be a _PROP_ASSERT()? */
    742 	if (pde == NULL)
    743 		return;
    744 
    745 	_prop_dictionary_remove(pd, pde, idx);
    746 }
    747 
    748 /*
    749  * prop_dictionary_remove_keysym --
    750  *	Remove a reference to an object stored in the dictionary at the
    751  *	location encoded by the keysym.
    752  */
    753 void
    754 prop_dictionary_remove_keysym(prop_dictionary_t pd,
    755 			      prop_dictionary_keysym_t pde)
    756 {
    757 	prop_dictionary_keysym_t opde;
    758 	unsigned int idx;
    759 
    760 	_PROP_ASSERT(prop_object_is_dictionary(pd));
    761 	_PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
    762 	_PROP_ASSERT(pde->pde_objref != NULL);
    763 
    764 	/* XXX Should this be a _PROP_ASSERT()? */
    765 	if (prop_dictionary_is_immutable(pd))
    766 		return;
    767 
    768 	opde = _prop_dict_lookup(pd, pde->pde_key, &idx);
    769 	_PROP_ASSERT(opde == pde);
    770 
    771 	_prop_dictionary_remove(pd, opde, idx);
    772 }
    773 
    774 /*
    775  * prop_dictionary_equals --
    776  *	Return TRUE if the two dictionaries are equivalent.  Note we do a
    777  *	by-value comparison of the objects in the dictionary.
    778  */
    779 boolean_t
    780 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
    781 {
    782 
    783 	return (_prop_dictionary_equals(dict1, dict2));
    784 }
    785 
    786 /*
    787  * prop_dictionary_keysym_cstring_nocopy --
    788  *	Return an immutable reference to the keysym's value.
    789  */
    790 const char *
    791 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pde)
    792 {
    793 
    794 	_PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
    795 	return (pde->pde_key);
    796 }
    797 
    798 /*
    799  * prop_dictionary_keysym_equals --
    800  *	Return TRUE if the two dictionary key symbols are equivalent.
    801  *	Note: We do not compare the object references.
    802  */
    803 boolean_t
    804 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pde1,
    805 			      prop_dictionary_keysym_t pde2)
    806 {
    807 
    808 	return (_prop_dict_entry_equals(pde1, pde2));
    809 }
    810 
    811 /*
    812  * prop_dictionary_externalize --
    813  *	Externalize a dictionary, returning a NUL-terminated buffer
    814  *	containing the XML-style representation.  The buffer is allocated
    815  *	with the M_TEMP memory type.
    816  */
    817 char *
    818 prop_dictionary_externalize(prop_dictionary_t pd)
    819 {
    820 	struct _prop_object_externalize_context *ctx;
    821 	char *cp;
    822 
    823 	ctx = _prop_object_externalize_context_alloc();
    824 	if (ctx == NULL)
    825 		return (NULL);
    826 
    827 	if (_prop_object_externalize_start_tag(ctx,
    828 					"plist version=\"1.0\"") == FALSE ||
    829 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE ||
    830 	    (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == FALSE ||
    831 	    _prop_object_externalize_end_tag(ctx, "plist") == FALSE ||
    832 	    _prop_object_externalize_append_char(ctx, '\0') == FALSE) {
    833 		/* We are responsible for releasing the buffer. */
    834 		_PROP_FREE(ctx->poec_buf, M_TEMP);
    835 		_prop_object_externalize_context_free(ctx);
    836 		return (NULL);
    837 	}
    838 
    839 	cp = ctx->poec_buf;
    840 	_prop_object_externalize_context_free(ctx);
    841 
    842 	return (cp);
    843 }
    844 
    845 /*
    846  * _prop_dictionary_internalize --
    847  *	Parse a <dict>...</dict> and return the object created from the
    848  *	external representation.
    849  */
    850 prop_object_t
    851 _prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
    852 {
    853 	prop_dictionary_t dict;
    854 	prop_object_t val;
    855 	size_t keylen;
    856 	char *tmpkey;
    857 
    858 	/* We don't currently understand any attributes. */
    859 	if (ctx->poic_tagattr != NULL)
    860 		return (NULL);
    861 
    862 	dict = prop_dictionary_create();
    863 	if (dict == NULL)
    864 		return (NULL);
    865 
    866 	if (ctx->poic_is_empty_element)
    867 		return (dict);
    868 
    869 	tmpkey = _PROP_MALLOC(PDE_MAXKEY + 1, M_TEMP);
    870 	if (tmpkey == NULL)
    871 		goto bad;
    872 
    873 	for (;;) {
    874 		/* Fetch the next tag. */
    875 		if (_prop_object_internalize_find_tag(ctx, NULL,
    876 					_PROP_TAG_TYPE_EITHER) == FALSE)
    877 			goto bad;
    878 
    879 		/* Check to see if this is the end of the dictionary. */
    880 		if (_PROP_TAG_MATCH(ctx, "dict") &&
    881 		    ctx->poic_tag_type == _PROP_TAG_TYPE_END)
    882 			break;
    883 
    884 		/* Ok, it must be a non-empty key start tag. */
    885 		if (!_PROP_TAG_MATCH(ctx, "key") ||
    886 		    ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
    887 		    ctx->poic_is_empty_element)
    888 		    	goto bad;
    889 
    890 		if (_prop_object_internalize_decode_string(ctx,
    891 						tmpkey, PDE_MAXKEY, &keylen,
    892 						&ctx->poic_cp) == FALSE)
    893 			goto bad;
    894 
    895 		_PROP_ASSERT(keylen <= PDE_MAXKEY);
    896 		tmpkey[keylen] = '\0';
    897 
    898 		if (_prop_object_internalize_find_tag(ctx, "key",
    899 					_PROP_TAG_TYPE_END) == FALSE)
    900 			goto bad;
    901 
    902 		/* ..and now the beginning of the value. */
    903 		if (_prop_object_internalize_find_tag(ctx, NULL,
    904 					_PROP_TAG_TYPE_START) == FALSE)
    905 			goto bad;
    906 
    907 		val = _prop_object_internalize_by_tag(ctx);
    908 		if (val == NULL)
    909 			goto bad;
    910 
    911 		if (prop_dictionary_set(dict, tmpkey, val) == FALSE) {
    912 			prop_object_release(val);
    913 			goto bad;
    914 		}
    915 		prop_object_release(val);
    916 	}
    917 
    918 	_PROP_FREE(tmpkey, M_TEMP);
    919 	return (dict);
    920 
    921  bad:
    922 	if (tmpkey != NULL)
    923 		_PROP_FREE(tmpkey, M_TEMP);
    924 	prop_object_release(dict);
    925 	return (NULL);
    926 }
    927 
    928 /*
    929  * prop_dictionary_internalize --
    930  *	Create a dictionary by parsing the NUL-terminated XML-style
    931  *	representation.
    932  */
    933 prop_dictionary_t
    934 prop_dictionary_internalize(const char *xml)
    935 {
    936 	prop_dictionary_t dict = NULL;
    937 	struct _prop_object_internalize_context *ctx;
    938 
    939 	ctx = _prop_object_internalize_context_alloc(xml);
    940 	if (ctx == NULL)
    941 		return (NULL);
    942 
    943 	/* We start with a <plist> tag. */
    944 	if (_prop_object_internalize_find_tag(ctx, "plist",
    945 					      _PROP_TAG_TYPE_START) == FALSE)
    946 		goto out;
    947 
    948 	/* Plist elements cannot be empty. */
    949 	if (ctx->poic_is_empty_element)
    950 		goto out;
    951 
    952 	/*
    953 	 * We don't understand any plist attributes, but Apple XML
    954 	 * property lists often have a "version" attibute.  If we
    955 	 * see that one, we simply ignore it.
    956 	 */
    957 	if (ctx->poic_tagattr != NULL &&
    958 	    !_PROP_TAGATTR_MATCH(ctx, "version"))
    959 		goto out;
    960 
    961 	/* Next we expect to see <dict>. */
    962 	if (_prop_object_internalize_find_tag(ctx, "dict",
    963 					      _PROP_TAG_TYPE_START) == FALSE)
    964 		goto out;
    965 
    966 	dict = _prop_dictionary_internalize(ctx);
    967 	if (dict == NULL)
    968 		goto out;
    969 
    970 	/* We've advanced past </dict>.  Now we want </plist>. */
    971 	if (_prop_object_internalize_find_tag(ctx, "plist",
    972 					      _PROP_TAG_TYPE_END) == FALSE) {
    973 		prop_object_release(dict);
    974 		dict = NULL;
    975 	}
    976 
    977  out:
    978  	_prop_object_internalize_context_free(ctx);
    979 	return (dict);
    980 }
    981