xref: /netbsd-src/common/lib/libprop/prop_dictionary.c (revision 8d23affcbe422d9873c2a51c987893246920eb41)
1 /*	$NetBSD: prop_dictionary.c,v 1.46 2023/06/14 00:35:18 rin 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*/
_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 references 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 	}
921 	pde = _prop_dict_lookup(pd, key, NULL);
922 	if (pde != NULL) {
923 		_PROP_ASSERT(pde->pde_objref != NULL);
924 		po = pde->pde_objref;
925 	}
926 	if (!locked) {
927 		_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
928 	}
929 	return (po);
930 }
931 /*
932  * prop_dictionary_get --
933  *	Return the object stored with specified key.
934  */
935 prop_object_t
prop_dictionary_get(prop_dictionary_t pd,const char * key)936 prop_dictionary_get(prop_dictionary_t pd, const char *key)
937 {
938 	prop_object_t po = NULL;
939 
940 	if (! prop_object_is_dictionary(pd))
941 		return (NULL);
942 
943 	_PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
944 	po = _prop_dictionary_get(pd, key, true);
945 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
946 	return (po);
947 }
948 
949 static prop_object_t
_prop_dictionary_get_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk,bool locked)950 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
951     bool locked)
952 {
953 
954 	if (! (prop_object_is_dictionary(pd) &&
955 	       prop_object_is_dictionary_keysym(pdk)))
956 		return (NULL);
957 
958 	return (_prop_dictionary_get(pd, pdk->pdk_key, locked));
959 }
960 
961 /*
962  * prop_dictionary_get_keysym --
963  *	Return the object stored at the location encoded by the keysym.
964  */
965 prop_object_t
prop_dictionary_get_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk)966 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
967 {
968 
969 	return (_prop_dictionary_get_keysym(pd, pdk, false));
970 }
971 
972 /*
973  * prop_dictionary_set --
974  *	Store a reference to an object at with the specified key.
975  *	If the key already exist, the original object is released.
976  */
977 bool
prop_dictionary_set(prop_dictionary_t pd,const char * key,prop_object_t po)978 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
979 {
980 	struct _prop_dict_entry *pde;
981 	prop_dictionary_keysym_t pdk;
982 	unsigned int idx;
983 	bool rv = false;
984 
985 	if (! prop_object_is_dictionary(pd))
986 		return (false);
987 
988 	_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
989 
990 	if (prop_dictionary_is_immutable(pd))
991 		return (false);
992 
993 	_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
994 
995 	pde = _prop_dict_lookup(pd, key, &idx);
996 	if (pde != NULL) {
997 		prop_object_t opo = pde->pde_objref;
998 		prop_object_retain(po);
999 		pde->pde_objref = po;
1000 		prop_object_release(opo);
1001 		rv = true;
1002 		goto out;
1003 	}
1004 
1005 	pdk = _prop_dict_keysym_alloc(key);
1006 	if (pdk == NULL)
1007 		goto out;
1008 
1009 	if (pd->pd_count == pd->pd_capacity &&
1010 	    _prop_dictionary_expand(pd,
1011 	    			    pd->pd_capacity + EXPAND_STEP) == false) {
1012 		prop_object_release(pdk);
1013 	    	goto out;
1014 	}
1015 
1016 	/* At this point, the store will succeed. */
1017 	prop_object_retain(po);
1018 
1019 	if (pd->pd_count == 0) {
1020 		pd->pd_array[0].pde_key = pdk;
1021 		pd->pd_array[0].pde_objref = po;
1022 		pd->pd_count++;
1023 		pd->pd_version++;
1024 		rv = true;
1025 		goto out;
1026 	}
1027 
1028 	pde = &pd->pd_array[idx];
1029 	_PROP_ASSERT(pde->pde_key != NULL);
1030 
1031 	if (strcmp(key, pde->pde_key->pdk_key) < 0) {
1032 		/*
1033 		 * key < pdk_key: insert to the left.  This is the same as
1034 		 * inserting to the right, except we decrement the current
1035 		 * index first.
1036 		 *
1037 		 * Because we're unsigned, we have to special case 0
1038 		 * (grumble).
1039 		 */
1040 		if (idx == 0) {
1041 			memmove(&pd->pd_array[1], &pd->pd_array[0],
1042 				pd->pd_count * sizeof(*pde));
1043 			pd->pd_array[0].pde_key = pdk;
1044 			pd->pd_array[0].pde_objref = po;
1045 			pd->pd_count++;
1046 			pd->pd_version++;
1047 			rv = true;
1048 			goto out;
1049 		}
1050 		idx--;
1051 	}
1052 
1053 	memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
1054 		(pd->pd_count - (idx + 1)) * sizeof(*pde));
1055 	pd->pd_array[idx + 1].pde_key = pdk;
1056 	pd->pd_array[idx + 1].pde_objref = po;
1057 	pd->pd_count++;
1058 
1059 	pd->pd_version++;
1060 
1061 	rv = true;
1062 
1063  out:
1064 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1065 	return (rv);
1066 }
1067 
1068 /*
1069  * prop_dictionary_set_keysym --
1070  *	Replace the object in the dictionary at the location encoded by
1071  *	the keysym.
1072  */
1073 bool
prop_dictionary_set_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk,prop_object_t po)1074 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
1075 			   prop_object_t po)
1076 {
1077 
1078 	if (! (prop_object_is_dictionary(pd) &&
1079 	       prop_object_is_dictionary_keysym(pdk)))
1080 		return (false);
1081 
1082 	return (prop_dictionary_set(pd, pdk->pdk_key, po));
1083 }
1084 
1085 static void
_prop_dictionary_remove(prop_dictionary_t pd,struct _prop_dict_entry * pde,unsigned int idx)1086 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
1087     unsigned int idx)
1088 {
1089 	prop_dictionary_keysym_t pdk = pde->pde_key;
1090 	prop_object_t po = pde->pde_objref;
1091 
1092 	/*
1093 	 * Dictionary must be WRITE-LOCKED.
1094 	 */
1095 
1096 	_PROP_ASSERT(pd->pd_count != 0);
1097 	_PROP_ASSERT(idx < pd->pd_count);
1098 	_PROP_ASSERT(pde == &pd->pd_array[idx]);
1099 
1100 	idx++;
1101 	memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
1102 		(pd->pd_count - idx) * sizeof(*pde));
1103 	pd->pd_count--;
1104 	pd->pd_version++;
1105 
1106 
1107 	prop_object_release(pdk);
1108 
1109 	prop_object_release(po);
1110 }
1111 
1112 /*
1113  * prop_dictionary_remove --
1114  *	Remove the reference to an object with the specified key from
1115  *	the dictionary.
1116  */
1117 void
prop_dictionary_remove(prop_dictionary_t pd,const char * key)1118 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
1119 {
1120 	struct _prop_dict_entry *pde;
1121 	unsigned int idx;
1122 
1123 	if (! prop_object_is_dictionary(pd))
1124 		return;
1125 
1126 	_PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1127 
1128 	/* XXX Should this be a _PROP_ASSERT()? */
1129 	if (prop_dictionary_is_immutable(pd))
1130 		goto out;
1131 
1132 	pde = _prop_dict_lookup(pd, key, &idx);
1133 	/* XXX Should this be a _PROP_ASSERT()? */
1134 	if (pde == NULL)
1135 		goto out;
1136 
1137 	_prop_dictionary_remove(pd, pde, idx);
1138  out:
1139 	_PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1140 }
1141 
1142 /*
1143  * prop_dictionary_remove_keysym --
1144  *	Remove a reference to an object stored in the dictionary at the
1145  *	location encoded by the keysym.
1146  */
1147 void
prop_dictionary_remove_keysym(prop_dictionary_t pd,prop_dictionary_keysym_t pdk)1148 prop_dictionary_remove_keysym(prop_dictionary_t pd,
1149 			      prop_dictionary_keysym_t pdk)
1150 {
1151 
1152 	if (! (prop_object_is_dictionary(pd) &&
1153 	       prop_object_is_dictionary_keysym(pdk)))
1154 		return;
1155 
1156 	prop_dictionary_remove(pd, pdk->pdk_key);
1157 }
1158 
1159 /*
1160  * prop_dictionary_equals --
1161  *	Return true if the two dictionaries are equivalent.  Note we do a
1162  *	by-value comparison of the objects in the dictionary.
1163  */
1164 bool
prop_dictionary_equals(prop_dictionary_t dict1,prop_dictionary_t dict2)1165 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
1166 {
1167 	if (!prop_object_is_dictionary(dict1) ||
1168 	    !prop_object_is_dictionary(dict2))
1169 		return (false);
1170 
1171 	return (prop_object_equals(dict1, dict2));
1172 }
1173 
1174 /*
1175  * prop_dictionary_keysym_value --
1176  *	Return a reference to the keysym's value.
1177  */
1178 const char *
prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk)1179 prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk)
1180 {
1181 
1182 	if (! prop_object_is_dictionary_keysym(pdk))
1183 		return (NULL);
1184 
1185 	return (pdk->pdk_key);
1186 }
1187 
1188 _PROP_DEPRECATED(prop_dictionary_keysym_cstring_nocopy,
1189     "this program uses prop_dictionary_keysym_cstring_nocopy(), "
1190     "which is deprecated; use prop_dictionary_keysym_value() instead.")
1191 const char *
prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)1192 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
1193 {
1194 
1195 	if (! prop_object_is_dictionary_keysym(pdk))
1196 		return (NULL);
1197 
1198 	return (pdk->pdk_key);
1199 }
1200 
1201 /*
1202  * prop_dictionary_keysym_equals --
1203  *	Return true if the two dictionary key symbols are equivalent.
1204  *	Note: We do not compare the object references.
1205  */
1206 bool
prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,prop_dictionary_keysym_t pdk2)1207 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
1208 			      prop_dictionary_keysym_t pdk2)
1209 {
1210 	if (!prop_object_is_dictionary_keysym(pdk1) ||
1211 	    !prop_object_is_dictionary_keysym(pdk2))
1212 		return (false);
1213 
1214 	return (prop_object_equals(pdk1, pdk2));
1215 }
1216 
1217 /*
1218  * prop_dictionary_externalize --
1219  *	Externalize a dictionary, returning a NUL-terminated buffer
1220  *	containing the XML-style representation.  The buffer is allocated
1221  *	with the M_TEMP memory type.
1222  */
1223 char *
prop_dictionary_externalize(prop_dictionary_t pd)1224 prop_dictionary_externalize(prop_dictionary_t pd)
1225 {
1226 	struct _prop_object_externalize_context *ctx;
1227 	char *cp;
1228 
1229 	ctx = _prop_object_externalize_context_alloc();
1230 	if (ctx == NULL)
1231 		return (NULL);
1232 
1233 	if (_prop_object_externalize_header(ctx) == false ||
1234 	    (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false ||
1235 	    _prop_object_externalize_footer(ctx) == false) {
1236 		/* We are responsible for releasing the buffer. */
1237 		_PROP_FREE(ctx->poec_buf, M_TEMP);
1238 		_prop_object_externalize_context_free(ctx);
1239 		return (NULL);
1240 	}
1241 
1242 	cp = ctx->poec_buf;
1243 	_prop_object_externalize_context_free(ctx);
1244 
1245 	return (cp);
1246 }
1247 
1248 /*
1249  * _prop_dictionary_internalize --
1250  *	Parse a <dict>...</dict> and return the object created from the
1251  *	external representation.
1252  *
1253  * Internal state in via rec_data is the storage area for the last processed
1254  * key.
1255  * _prop_dictionary_internalize_body is the upper half of the parse loop.
1256  * It is responsible for parsing the key directly and storing it in the area
1257  * referenced by rec_data.
1258  * _prop_dictionary_internalize_cont is the lower half and called with the value
1259  * associated with the key.
1260  */
1261 static bool _prop_dictionary_internalize_body(prop_stack_t,
1262     prop_object_t *, struct _prop_object_internalize_context *, char *);
1263 
1264 bool
_prop_dictionary_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)1265 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
1266     struct _prop_object_internalize_context *ctx)
1267 {
1268 	prop_dictionary_t dict;
1269 	char *tmpkey;
1270 
1271 	/* We don't currently understand any attributes. */
1272 	if (ctx->poic_tagattr != NULL)
1273 		return (true);
1274 
1275 	dict = prop_dictionary_create();
1276 	if (dict == NULL)
1277 		return (true);
1278 
1279 	if (ctx->poic_is_empty_element) {
1280 		*obj = dict;
1281 		return (true);
1282 	}
1283 
1284 	tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1285 	if (tmpkey == NULL) {
1286 		prop_object_release(dict);
1287 		return (true);
1288 	}
1289 
1290 	*obj = dict;
1291 	/*
1292 	 * Opening tag is found, storage for key allocated and
1293 	 * now continue to the first element.
1294 	 */
1295 	return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1296 }
1297 
1298 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)1299 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
1300     struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
1301 {
1302 	prop_dictionary_t dict = *obj;
1303 	char *tmpkey = data;
1304 
1305 	_PROP_ASSERT(tmpkey != NULL);
1306 
1307 	if (child == NULL ||
1308 	    prop_dictionary_set(dict, tmpkey, child) == false) {
1309 		_PROP_FREE(tmpkey, M_TEMP);
1310 		if (child != NULL)
1311 			prop_object_release(child);
1312 		prop_object_release(dict);
1313 		*obj = NULL;
1314 		return (true);
1315 	}
1316 
1317 	prop_object_release(child);
1318 
1319 	/*
1320 	 * key, value was added, now continue looking for the next key
1321 	 * or the closing tag.
1322 	 */
1323 	return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1324 }
1325 
1326 static bool
_prop_dictionary_internalize_body(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx,char * tmpkey)1327 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
1328     struct _prop_object_internalize_context *ctx, char *tmpkey)
1329 {
1330 	prop_dictionary_t dict = *obj;
1331 	size_t keylen;
1332 
1333 	/* Fetch the next tag. */
1334 	if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
1335 		goto bad;
1336 
1337 	/* Check to see if this is the end of the dictionary. */
1338 	if (_PROP_TAG_MATCH(ctx, "dict") &&
1339 	    ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
1340 		_PROP_FREE(tmpkey, M_TEMP);
1341 		return (true);
1342 	}
1343 
1344 	/* Ok, it must be a non-empty key start tag. */
1345 	if (!_PROP_TAG_MATCH(ctx, "key") ||
1346 	    ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
1347 	    ctx->poic_is_empty_element)
1348 	    	goto bad;
1349 
1350 	if (_prop_object_internalize_decode_string(ctx,
1351 					tmpkey, PDK_MAXKEY, &keylen,
1352 					&ctx->poic_cp) == false)
1353 		goto bad;
1354 
1355 	_PROP_ASSERT(keylen <= PDK_MAXKEY);
1356 	tmpkey[keylen] = '\0';
1357 
1358 	if (_prop_object_internalize_find_tag(ctx, "key",
1359 				_PROP_TAG_TYPE_END) == false)
1360 		goto bad;
1361 
1362 	/* ..and now the beginning of the value. */
1363 	if (_prop_object_internalize_find_tag(ctx, NULL,
1364 				_PROP_TAG_TYPE_START) == false)
1365 		goto bad;
1366 
1367 	/*
1368 	 * Key is found, now wait for value to be parsed.
1369 	 */
1370 	if (_prop_stack_push(stack, *obj,
1371 			     _prop_dictionary_internalize_continue,
1372 			     tmpkey, NULL))
1373 		return (false);
1374 
1375  bad:
1376 	_PROP_FREE(tmpkey, M_TEMP);
1377 	prop_object_release(dict);
1378 	*obj = NULL;
1379 	return (true);
1380 }
1381 
1382 /*
1383  * prop_dictionary_internalize --
1384  *	Create a dictionary by parsing the NUL-terminated XML-style
1385  *	representation.
1386  */
1387 prop_dictionary_t
prop_dictionary_internalize(const char * xml)1388 prop_dictionary_internalize(const char *xml)
1389 {
1390 	return _prop_generic_internalize(xml, "dict");
1391 }
1392 
1393 #if !defined(_KERNEL) && !defined(_STANDALONE)
1394 /*
1395  * prop_dictionary_externalize_to_file --
1396  *	Externalize a dictionary to the specified file.
1397  */
1398 bool
prop_dictionary_externalize_to_file(prop_dictionary_t dict,const char * fname)1399 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1400 {
1401 	char *xml;
1402 	bool rv;
1403 	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
1404 
1405 	xml = prop_dictionary_externalize(dict);
1406 	if (xml == NULL)
1407 		return (false);
1408 	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
1409 	if (rv == false)
1410 		save_errno = errno;
1411 	_PROP_FREE(xml, M_TEMP);
1412 	if (rv == false)
1413 		errno = save_errno;
1414 
1415 	return (rv);
1416 }
1417 
1418 /*
1419  * prop_dictionary_internalize_from_file --
1420  *	Internalize a dictionary from a file.
1421  */
1422 prop_dictionary_t
prop_dictionary_internalize_from_file(const char * fname)1423 prop_dictionary_internalize_from_file(const char *fname)
1424 {
1425 	struct _prop_object_internalize_mapped_file *mf;
1426 	prop_dictionary_t dict;
1427 
1428 	mf = _prop_object_internalize_map_file(fname);
1429 	if (mf == NULL)
1430 		return (NULL);
1431 	dict = prop_dictionary_internalize(mf->poimf_xml);
1432 	_prop_object_internalize_unmap_file(mf);
1433 
1434 	return (dict);
1435 }
1436 #endif /* !_KERNEL && !_STANDALONE */
1437