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