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