1 /*	$NetBSD: prop_dictionary.c,v 1.41 2016/06/28 05:21:15 pgoyette Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006, 2007 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*/
_prop_dict_keysym_rb_compare_nodes(void * ctx _PROP_ARG_UNUSED,const void * n1,const void * n2)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*/
_prop_dict_keysym_rb_compare_key(void * ctx _PROP_ARG_UNUSED,const void * n,const void * v)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)
_PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex)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
_prop_dict_keysym_put(prop_dictionary_keysym_t pdk)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
_prop_dict_keysym_free(prop_stack_t stack,prop_object_t * obj)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
_prop_dict_keysym_externalize(struct _prop_object_externalize_context * ctx,void * v)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
_prop_dict_keysym_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)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
_prop_dict_keysym_alloc(const char * key)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
_prop_dictionary_free(prop_stack_t stack,prop_object_t * obj)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
_prop_dictionary_lock(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
_prop_dictionary_unlock(void)407 _prop_dictionary_unlock(void)
408 {
409 	_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
410 }
411 
412 static void
_prop_dictionary_emergency_free(prop_object_t obj)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
_prop_dictionary_externalize(struct _prop_object_externalize_context * ctx,void * v)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
_prop_dictionary_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)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
_prop_dictionary_equals_finish(prop_object_t v1,prop_object_t v2)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
_prop_dictionary_alloc(unsigned int capacity)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
_prop_dictionary_expand(prop_dictionary_t pd,unsigned int capacity)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
_prop_dictionary_iterator_next_object_locked(void * v)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
_prop_dictionary_iterator_next_object(void * v)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
_prop_dictionary_iterator_reset_locked(void * v)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
_prop_dictionary_iterator_reset(void * v)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
prop_dictionary_create(void)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
prop_dictionary_create_with_capacity(unsigned int capacity)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
prop_dictionary_copy(prop_dictionary_t opd)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
prop_dictionary_copy_mutable(prop_dictionary_t opd)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
prop_dictionary_make_immutable(prop_dictionary_t pd)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
prop_dictionary_count(prop_dictionary_t pd)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
prop_dictionary_ensure_capacity(prop_dictionary_t pd,unsigned int capacity)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
_prop_dictionary_iterator_locked(prop_dictionary_t pd)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
prop_dictionary_iterator(prop_dictionary_t pd)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
prop_dictionary_all_keys(prop_dictionary_t pd)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 *
_prop_dict_lookup(prop_dictionary_t pd,const char * key,unsigned int * idxp)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
_prop_dictionary_get(prop_dictionary_t pd,const char * key,bool locked)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
prop_dictionary_get(prop_dictionary_t pd,const char * key)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
_prop_dictionary_get_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk,bool locked)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
prop_dictionary_get_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk)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
prop_dictionary_set(prop_dictionary_t pd,const char * key,prop_object_t po)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
prop_dictionary_set_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk,prop_object_t po)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
_prop_dictionary_remove(prop_dictionary_t pd,struct _prop_dict_entry * pde,unsigned int idx)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
prop_dictionary_remove(prop_dictionary_t pd,const char * key)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
prop_dictionary_remove_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk)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
prop_dictionary_equals(prop_dictionary_t dict1,prop_dictionary_t dict2)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_cstring_nocopy --
1174  *	Return an immutable reference to the keysym's value.
1175  */
1176 const char *
prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)1177 prop_dictionary_keysym_cstring_nocopy(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 /*
1187  * prop_dictionary_keysym_equals --
1188  *	Return true if the two dictionary key symbols are equivalent.
1189  *	Note: We do not compare the object references.
1190  */
1191 bool
prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,prop_dictionary_keysym_t pdk2)1192 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
1193 			      prop_dictionary_keysym_t pdk2)
1194 {
1195 	if (!prop_object_is_dictionary_keysym(pdk1) ||
1196 	    !prop_object_is_dictionary_keysym(pdk2))
1197 		return (false);
1198 
1199 	return (prop_object_equals(pdk1, pdk2));
1200 }
1201 
1202 /*
1203  * prop_dictionary_externalize --
1204  *	Externalize a dictionary, returning a NUL-terminated buffer
1205  *	containing the XML-style representation.  The buffer is allocated
1206  *	with the M_TEMP memory type.
1207  */
1208 char *
prop_dictionary_externalize(prop_dictionary_t pd)1209 prop_dictionary_externalize(prop_dictionary_t pd)
1210 {
1211 	struct _prop_object_externalize_context *ctx;
1212 	char *cp;
1213 
1214 	ctx = _prop_object_externalize_context_alloc();
1215 	if (ctx == NULL)
1216 		return (NULL);
1217 
1218 	if (_prop_object_externalize_header(ctx) == false ||
1219 	    (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false ||
1220 	    _prop_object_externalize_footer(ctx) == false) {
1221 		/* We are responsible for releasing the buffer. */
1222 		_PROP_FREE(ctx->poec_buf, M_TEMP);
1223 		_prop_object_externalize_context_free(ctx);
1224 		return (NULL);
1225 	}
1226 
1227 	cp = ctx->poec_buf;
1228 	_prop_object_externalize_context_free(ctx);
1229 
1230 	return (cp);
1231 }
1232 
1233 /*
1234  * _prop_dictionary_internalize --
1235  *	Parse a <dict>...</dict> and return the object created from the
1236  *	external representation.
1237  *
1238  * Internal state in via rec_data is the storage area for the last processed
1239  * key.
1240  * _prop_dictionary_internalize_body is the upper half of the parse loop.
1241  * It is responsible for parsing the key directly and storing it in the area
1242  * referenced by rec_data.
1243  * _prop_dictionary_internalize_cont is the lower half and called with the value
1244  * associated with the key.
1245  */
1246 static bool _prop_dictionary_internalize_body(prop_stack_t,
1247     prop_object_t *, struct _prop_object_internalize_context *, char *);
1248 
1249 bool
_prop_dictionary_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)1250 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
1251     struct _prop_object_internalize_context *ctx)
1252 {
1253 	prop_dictionary_t dict;
1254 	char *tmpkey;
1255 
1256 	/* We don't currently understand any attributes. */
1257 	if (ctx->poic_tagattr != NULL)
1258 		return (true);
1259 
1260 	dict = prop_dictionary_create();
1261 	if (dict == NULL)
1262 		return (true);
1263 
1264 	if (ctx->poic_is_empty_element) {
1265 		*obj = dict;
1266 		return (true);
1267 	}
1268 
1269 	tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1270 	if (tmpkey == NULL) {
1271 		prop_object_release(dict);
1272 		return (true);
1273 	}
1274 
1275 	*obj = dict;
1276 	/*
1277 	 * Opening tag is found, storage for key allocated and
1278 	 * now continue to the first element.
1279 	 */
1280 	return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1281 }
1282 
1283 static bool
_prop_dictionary_internalize_continue(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx,void * data,prop_object_t child)1284 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
1285     struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
1286 {
1287 	prop_dictionary_t dict = *obj;
1288 	char *tmpkey = data;
1289 
1290 	_PROP_ASSERT(tmpkey != NULL);
1291 
1292 	if (child == NULL ||
1293 	    prop_dictionary_set(dict, tmpkey, child) == false) {
1294 		_PROP_FREE(tmpkey, M_TEMP);
1295 		if (child != NULL)
1296 			prop_object_release(child);
1297 		prop_object_release(dict);
1298 		*obj = NULL;
1299 		return (true);
1300 	}
1301 
1302 	prop_object_release(child);
1303 
1304 	/*
1305 	 * key, value was added, now continue looking for the next key
1306 	 * or the closing tag.
1307 	 */
1308 	return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1309 }
1310 
1311 static bool
_prop_dictionary_internalize_body(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx,char * tmpkey)1312 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
1313     struct _prop_object_internalize_context *ctx, char *tmpkey)
1314 {
1315 	prop_dictionary_t dict = *obj;
1316 	size_t keylen;
1317 
1318 	/* Fetch the next tag. */
1319 	if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
1320 		goto bad;
1321 
1322 	/* Check to see if this is the end of the dictionary. */
1323 	if (_PROP_TAG_MATCH(ctx, "dict") &&
1324 	    ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
1325 		_PROP_FREE(tmpkey, M_TEMP);
1326 		return (true);
1327 	}
1328 
1329 	/* Ok, it must be a non-empty key start tag. */
1330 	if (!_PROP_TAG_MATCH(ctx, "key") ||
1331 	    ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
1332 	    ctx->poic_is_empty_element)
1333 	    	goto bad;
1334 
1335 	if (_prop_object_internalize_decode_string(ctx,
1336 					tmpkey, PDK_MAXKEY, &keylen,
1337 					&ctx->poic_cp) == false)
1338 		goto bad;
1339 
1340 	_PROP_ASSERT(keylen <= PDK_MAXKEY);
1341 	tmpkey[keylen] = '\0';
1342 
1343 	if (_prop_object_internalize_find_tag(ctx, "key",
1344 				_PROP_TAG_TYPE_END) == false)
1345 		goto bad;
1346 
1347 	/* ..and now the beginning of the value. */
1348 	if (_prop_object_internalize_find_tag(ctx, NULL,
1349 				_PROP_TAG_TYPE_START) == false)
1350 		goto bad;
1351 
1352 	/*
1353 	 * Key is found, now wait for value to be parsed.
1354 	 */
1355 	if (_prop_stack_push(stack, *obj,
1356 			     _prop_dictionary_internalize_continue,
1357 			     tmpkey, NULL))
1358 		return (false);
1359 
1360  bad:
1361 	_PROP_FREE(tmpkey, M_TEMP);
1362 	prop_object_release(dict);
1363 	*obj = NULL;
1364 	return (true);
1365 }
1366 
1367 /*
1368  * prop_dictionary_internalize --
1369  *	Create a dictionary by parsing the NUL-terminated XML-style
1370  *	representation.
1371  */
1372 prop_dictionary_t
prop_dictionary_internalize(const char * xml)1373 prop_dictionary_internalize(const char *xml)
1374 {
1375 	return _prop_generic_internalize(xml, "dict");
1376 }
1377 
1378 #if !defined(_KERNEL) && !defined(_STANDALONE)
1379 /*
1380  * prop_dictionary_externalize_to_file --
1381  *	Externalize a dictionary to the specified file.
1382  */
1383 bool
prop_dictionary_externalize_to_file(prop_dictionary_t dict,const char * fname)1384 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1385 {
1386 	char *xml;
1387 	bool rv;
1388 	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
1389 
1390 	xml = prop_dictionary_externalize(dict);
1391 	if (xml == NULL)
1392 		return (false);
1393 	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
1394 	if (rv == false)
1395 		save_errno = errno;
1396 	_PROP_FREE(xml, M_TEMP);
1397 	if (rv == false)
1398 		errno = save_errno;
1399 
1400 	return (rv);
1401 }
1402 
1403 /*
1404  * prop_dictionary_internalize_from_file --
1405  *	Internalize a dictionary from a file.
1406  */
1407 prop_dictionary_t
prop_dictionary_internalize_from_file(const char * fname)1408 prop_dictionary_internalize_from_file(const char *fname)
1409 {
1410 	struct _prop_object_internalize_mapped_file *mf;
1411 	prop_dictionary_t dict;
1412 
1413 	mf = _prop_object_internalize_map_file(fname);
1414 	if (mf == NULL)
1415 		return (NULL);
1416 	dict = prop_dictionary_internalize(mf->poimf_xml);
1417 	_prop_object_internalize_unmap_file(mf);
1418 
1419 	return (dict);
1420 }
1421 #endif /* !_KERNEL && !_STANDALONE */
1422