prop_dictionary.c revision 1.13 1 /* $NetBSD: prop_dictionary.c,v 1.13 2006/10/03 15:45:04 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <prop/prop_dictionary.h>
40 #include <prop/prop_string.h>
41 #include "prop_object_impl.h"
42 #include "prop_rb_impl.h"
43
44 #if !defined(_KERNEL) && !defined(_STANDALONE)
45 #include <errno.h>
46 #endif
47
48 /*
49 * We implement these like arrays, but we keep them sorted by key.
50 * This allows us to binary-search as well as keep externalized output
51 * sane-looking for human eyes.
52 */
53
54 #define EXPAND_STEP 16
55
56 /*
57 * prop_dictionary_keysym_t is allocated with space at the end to hold the
58 * key. This must be a regular object so that we can maintain sane iterator
59 * semantics -- we don't want to require that the caller release the result
60 * of prop_object_iterator_next().
61 *
62 * We'd like to have some small'ish keysym objects for up-to-16 characters
63 * in a key, some for up-to-32 characters in a key, and then a final bucket
64 * for up-to-128 characters in a key (not including NUL). Keys longer than
65 * 128 characters are not allowed.
66 */
67 struct _prop_dictionary_keysym {
68 struct _prop_object pdk_obj;
69 size_t pdk_size;
70 struct rb_node pdk_link;
71 char pdk_key[1];
72 /* actually variable length */
73 };
74
75 #define RBNODE_TO_PDK(n) \
76 ((struct _prop_dictionary_keysym *) \
77 ((uintptr_t)n - offsetof(struct _prop_dictionary_keysym, pdk_link)))
78
79 /* pdk_key[1] takes care of the NUL */
80 #define PDK_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16)
81 #define PDK_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32)
82 #define PDK_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128)
83
84 #define PDK_MAXKEY 128
85
86 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16")
87 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32")
88 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128")
89
90 struct _prop_dict_entry {
91 prop_dictionary_keysym_t pde_key;
92 prop_object_t pde_objref;
93 };
94
95 struct _prop_dictionary {
96 struct _prop_object pd_obj;
97 _PROP_RWLOCK_DECL(pd_rwlock)
98 struct _prop_dict_entry *pd_array;
99 unsigned int pd_capacity;
100 unsigned int pd_count;
101 int pd_flags;
102
103 uint32_t pd_version;
104 };
105
106 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */
107
108 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
109 "propdict")
110 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
111 "property dictionary container object")
112
113 static void _prop_dictionary_free(void *);
114 static boolean_t _prop_dictionary_externalize(
115 struct _prop_object_externalize_context *,
116 void *);
117 static boolean_t _prop_dictionary_equals(void *, void *);
118
119 static const struct _prop_object_type _prop_object_type_dictionary = {
120 .pot_type = PROP_TYPE_DICTIONARY,
121 .pot_free = _prop_dictionary_free,
122 .pot_extern = _prop_dictionary_externalize,
123 .pot_equals = _prop_dictionary_equals,
124 };
125
126 static void _prop_dict_keysym_free(void *);
127 static boolean_t _prop_dict_keysym_externalize(
128 struct _prop_object_externalize_context *,
129 void *);
130 static boolean_t _prop_dict_keysym_equals(void *, void *);
131
132 static const struct _prop_object_type _prop_object_type_dict_keysym = {
133 .pot_type = PROP_TYPE_DICT_KEYSYM,
134 .pot_free = _prop_dict_keysym_free,
135 .pot_extern = _prop_dict_keysym_externalize,
136 .pot_equals = _prop_dict_keysym_equals,
137 };
138
139 #define prop_object_is_dictionary(x) \
140 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary)
141 #define prop_object_is_dictionary_keysym(x) \
142 ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym)
143
144 #define prop_dictionary_is_immutable(x) \
145 (((x)->pd_flags & PD_F_IMMUTABLE) != 0)
146
147 struct _prop_dictionary_iterator {
148 struct _prop_object_iterator pdi_base;
149 unsigned int pdi_index;
150 };
151
152 /*
153 * Dictionary key symbols are immutable, and we are likely to have many
154 * duplicated key symbols. So, to save memory, we unique'ify key symbols
155 * so we only have to have one copy of each string.
156 */
157
158 static int
159 _prop_dict_keysym_rb_compare_nodes(const struct rb_node *n1,
160 const struct rb_node *n2)
161 {
162 const prop_dictionary_keysym_t pdk1 = RBNODE_TO_PDK(n1);
163 const prop_dictionary_keysym_t pdk2 = RBNODE_TO_PDK(n2);
164
165 return (strcmp(pdk1->pdk_key, pdk2->pdk_key));
166 }
167
168 static int
169 _prop_dict_keysym_rb_compare_key(const struct rb_node *n,
170 const void *v)
171 {
172 const prop_dictionary_keysym_t pdk = RBNODE_TO_PDK(n);
173 const char *cp = v;
174
175 return (strcmp(pdk->pdk_key, cp));
176 }
177
178 static const struct rb_tree_ops _prop_dict_keysym_rb_tree_ops = {
179 .rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes,
180 .rbto_compare_key = _prop_dict_keysym_rb_compare_key,
181 };
182
183 static struct rb_tree _prop_dict_keysym_tree;
184 static boolean_t _prop_dict_keysym_tree_initialized;
185
186 _PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex)
187
188 static void
189 _prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
190 {
191
192 if (pdk->pdk_size <= PDK_SIZE_16)
193 _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk);
194 else if (pdk->pdk_size <= PDK_SIZE_32)
195 _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk);
196 else {
197 _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128);
198 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk);
199 }
200 }
201
202 static void
203 _prop_dict_keysym_free(void *v)
204 {
205 prop_dictionary_keysym_t pdk = v;
206
207 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
208 _prop_rb_tree_remove_node(&_prop_dict_keysym_tree, &pdk->pdk_link);
209 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
210
211 _prop_dict_keysym_put(pdk);
212 }
213
214 static boolean_t
215 _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
216 void *v)
217 {
218 prop_dictionary_keysym_t pdk = v;
219
220 /* We externalize these as strings, and they're never empty. */
221
222 _PROP_ASSERT(pdk->pdk_key[0] != '\0');
223
224 if (_prop_object_externalize_start_tag(ctx, "string") == FALSE ||
225 _prop_object_externalize_append_encoded_cstring(ctx,
226 pdk->pdk_key) == FALSE ||
227 _prop_object_externalize_end_tag(ctx, "string") == FALSE)
228 return (FALSE);
229
230 return (TRUE);
231 }
232
233 static boolean_t
234 _prop_dict_keysym_equals(void *v1, void *v2)
235 {
236 prop_dictionary_keysym_t pdk1 = v1;
237 prop_dictionary_keysym_t pdk2 = v2;
238
239 if (! (prop_object_is_dictionary_keysym(pdk1) &&
240 prop_object_is_dictionary_keysym(pdk2)))
241 return (FALSE);
242
243 /*
244 * There is only ever one copy of a keysym at any given time,
245 * so we can reduce this to a simple pointer equality check.
246 */
247 return (pdk1 == pdk2);
248 }
249
250 static prop_dictionary_keysym_t
251 _prop_dict_keysym_alloc(const char *key)
252 {
253 prop_dictionary_keysym_t opdk, pdk;
254 const struct rb_node *n;
255 size_t size;
256
257 /*
258 * Check to see if this already exists in the tree. If it does,
259 * we just retain it and return it.
260 */
261 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
262 if (! _prop_dict_keysym_tree_initialized) {
263 _prop_rb_tree_init(&_prop_dict_keysym_tree,
264 &_prop_dict_keysym_rb_tree_ops);
265 _prop_dict_keysym_tree_initialized = TRUE;
266 } else {
267 n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
268 if (n != NULL) {
269 opdk = RBNODE_TO_PDK(n);
270 prop_object_retain(opdk);
271 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
272 return (opdk);
273 }
274 }
275 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
276
277 /*
278 * Not in the tree. Create it now.
279 */
280
281 size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */;
282
283 if (size <= PDK_SIZE_16)
284 pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
285 else if (size <= PDK_SIZE_32)
286 pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
287 else if (size <= PDK_SIZE_128)
288 pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
289 else
290 pdk = NULL; /* key too long */
291
292 if (pdk == NULL)
293 return (NULL);
294
295 _prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym);
296
297 strcpy(pdk->pdk_key, key);
298 pdk->pdk_size = size;
299
300 /*
301 * We dropped the mutex when we allocated the new object, so
302 * we have to check again if it is in the tree.
303 */
304 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
305 n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
306 if (n != NULL) {
307 opdk = RBNODE_TO_PDK(n);
308 prop_object_retain(opdk);
309 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
310 _prop_dict_keysym_put(pdk);
311 return (opdk);
312 }
313 _prop_rb_tree_insert_node(&_prop_dict_keysym_tree, &pdk->pdk_link);
314 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
315 return (pdk);
316 }
317
318 static void
319 _prop_dictionary_free(void *v)
320 {
321 prop_dictionary_t pd = v;
322 prop_dictionary_keysym_t pdk;
323 prop_object_t po;
324 unsigned int idx;
325
326 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
327 _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
328 (pd->pd_capacity != 0 && pd->pd_array != NULL));
329
330 for (idx = 0; idx < pd->pd_count; idx++) {
331 pdk = pd->pd_array[idx].pde_key;
332 _PROP_ASSERT(pdk != NULL);
333 prop_object_release(pdk);
334 po = pd->pd_array[idx].pde_objref;
335 _PROP_ASSERT(po != NULL);
336 prop_object_release(po);
337 }
338
339 if (pd->pd_array != NULL)
340 _PROP_FREE(pd->pd_array, M_PROP_DICT);
341
342 _PROP_RWLOCK_DESTROY(pd->pd_rwlock);
343
344 _PROP_POOL_PUT(_prop_dictionary_pool, pd);
345 }
346
347 static boolean_t
348 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
349 void *v)
350 {
351 prop_dictionary_t pd = v;
352 prop_dictionary_keysym_t pdk;
353 struct _prop_object *po;
354 prop_object_iterator_t pi;
355 unsigned int i;
356 boolean_t rv = FALSE;
357
358 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
359
360 if (pd->pd_count == 0) {
361 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
362 return (_prop_object_externalize_empty_tag(ctx, "dict"));
363 }
364
365 if (_prop_object_externalize_start_tag(ctx, "dict") == FALSE ||
366 _prop_object_externalize_append_char(ctx, '\n') == FALSE)
367 goto out;
368
369 pi = prop_dictionary_iterator(pd);
370 if (pi == NULL)
371 goto out;
372
373 ctx->poec_depth++;
374 _PROP_ASSERT(ctx->poec_depth != 0);
375
376 while ((pdk = prop_object_iterator_next(pi)) != NULL) {
377 po = prop_dictionary_get_keysym(pd, pdk);
378 if (po == NULL ||
379 _prop_object_externalize_start_tag(ctx, "key") == FALSE ||
380 _prop_object_externalize_append_encoded_cstring(ctx,
381 pdk->pdk_key) == FALSE ||
382 _prop_object_externalize_end_tag(ctx, "key") == FALSE ||
383 (*po->po_type->pot_extern)(ctx, po) == FALSE) {
384 prop_object_iterator_release(pi);
385 goto out;
386 }
387 }
388
389 prop_object_iterator_release(pi);
390
391 ctx->poec_depth--;
392 for (i = 0; i < ctx->poec_depth; i++) {
393 if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
394 goto out;
395 }
396 if (_prop_object_externalize_end_tag(ctx, "dict") == FALSE)
397 goto out;
398
399 rv = TRUE;
400
401 out:
402 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
403 return (rv);
404 }
405
406 static boolean_t
407 _prop_dictionary_equals(void *v1, void *v2)
408 {
409 prop_dictionary_t dict1 = v1;
410 prop_dictionary_t dict2 = v2;
411 const struct _prop_dict_entry *pde1, *pde2;
412 unsigned int idx;
413 boolean_t rv = FALSE;
414
415 if (! (prop_object_is_dictionary(dict1) &&
416 prop_object_is_dictionary(dict2)))
417 return (FALSE);
418
419 if (dict1 == dict2)
420 return (TRUE);
421
422 if ((uintptr_t)dict1 < (uintptr_t)dict2) {
423 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
424 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
425 } else {
426 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
427 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
428 }
429
430 if (dict1->pd_count != dict2->pd_count)
431 goto out;
432
433 for (idx = 0; idx < dict1->pd_count; idx++) {
434 pde1 = &dict1->pd_array[idx];
435 pde2 = &dict2->pd_array[idx];
436
437 if (prop_dictionary_keysym_equals(pde1->pde_key,
438 pde2->pde_key) == FALSE)
439 goto out;
440 if (prop_object_equals(pde1->pde_objref,
441 pde2->pde_objref) == FALSE)
442 goto out;
443 }
444
445 rv = TRUE;
446
447 out:
448 _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
449 _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
450 return (rv);
451 }
452
453 static prop_dictionary_t
454 _prop_dictionary_alloc(unsigned int capacity)
455 {
456 prop_dictionary_t pd;
457 struct _prop_dict_entry *array;
458
459 if (capacity != 0) {
460 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
461 if (array == NULL)
462 return (NULL);
463 } else
464 array = NULL;
465
466 pd = _PROP_POOL_GET(_prop_dictionary_pool);
467 if (pd != NULL) {
468 _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
469
470 _PROP_RWLOCK_INIT(pd->pd_rwlock);
471 pd->pd_array = array;
472 pd->pd_capacity = capacity;
473 pd->pd_count = 0;
474 pd->pd_flags = 0;
475
476 pd->pd_version = 0;
477 } else if (array != NULL)
478 _PROP_FREE(array, M_PROP_DICT);
479
480 return (pd);
481 }
482
483 static boolean_t
484 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
485 {
486 struct _prop_dict_entry *array, *oarray;
487
488 /*
489 * Dictionary must be WRITE-LOCKED.
490 */
491
492 oarray = pd->pd_array;
493
494 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
495 if (array == NULL)
496 return (FALSE);
497 if (oarray != NULL)
498 memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
499 pd->pd_array = array;
500 pd->pd_capacity = capacity;
501
502 if (oarray != NULL)
503 _PROP_FREE(oarray, M_PROP_DICT);
504
505 return (TRUE);
506 }
507
508 static prop_object_t
509 _prop_dictionary_iterator_next_object(void *v)
510 {
511 struct _prop_dictionary_iterator *pdi = v;
512 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
513 prop_dictionary_keysym_t pdk = NULL;
514
515 _PROP_ASSERT(prop_object_is_dictionary(pd));
516
517 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
518
519 if (pd->pd_version != pdi->pdi_base.pi_version)
520 goto out; /* dictionary changed during iteration */
521
522 _PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
523
524 if (pdi->pdi_index == pd->pd_count)
525 goto out; /* we've iterated all objects */
526
527 pdk = pd->pd_array[pdi->pdi_index].pde_key;
528 pdi->pdi_index++;
529
530 out:
531 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
532 return (pdk);
533 }
534
535 static void
536 _prop_dictionary_iterator_reset(void *v)
537 {
538 struct _prop_dictionary_iterator *pdi = v;
539 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
540
541 _PROP_ASSERT(prop_object_is_dictionary(pd));
542
543 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
544
545 pdi->pdi_index = 0;
546 pdi->pdi_base.pi_version = pd->pd_version;
547
548 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
549 }
550
551 /*
552 * prop_dictionary_create --
553 * Create a dictionary.
554 */
555 prop_dictionary_t
556 prop_dictionary_create(void)
557 {
558
559 return (_prop_dictionary_alloc(0));
560 }
561
562 /*
563 * prop_dictionary_create_with_capacity --
564 * Create a dictionary with the capacity to store N objects.
565 */
566 prop_dictionary_t
567 prop_dictionary_create_with_capacity(unsigned int capacity)
568 {
569
570 return (_prop_dictionary_alloc(capacity));
571 }
572
573 /*
574 * prop_dictionary_copy --
575 * Copy a dictionary. The new dictionary has an initial capacity equal
576 * to the number of objects stored int the original dictionary. The new
577 * dictionary contains refrences to the original dictionary's objects,
578 * not copies of those objects (i.e. a shallow copy).
579 */
580 prop_dictionary_t
581 prop_dictionary_copy(prop_dictionary_t opd)
582 {
583 prop_dictionary_t pd;
584 prop_dictionary_keysym_t pdk;
585 prop_object_t po;
586 unsigned int idx;
587
588 if (! prop_object_is_dictionary(opd))
589 return (NULL);
590
591 _PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
592
593 pd = _prop_dictionary_alloc(opd->pd_count);
594 if (pd != NULL) {
595 for (idx = 0; idx < opd->pd_count; idx++) {
596 pdk = opd->pd_array[idx].pde_key;
597 po = opd->pd_array[idx].pde_objref;
598
599 prop_object_retain(pdk);
600 prop_object_retain(po);
601
602 pd->pd_array[idx].pde_key = pdk;
603 pd->pd_array[idx].pde_objref = po;
604 }
605 pd->pd_count = opd->pd_count;
606 pd->pd_flags = opd->pd_flags;
607 }
608 _PROP_RWLOCK_UNLOCK(opd->pd_rwlock);
609 return (pd);
610 }
611
612 /*
613 * prop_dictionary_copy_mutable --
614 * Like prop_dictionary_copy(), but the resulting dictionary is
615 * mutable.
616 */
617 prop_dictionary_t
618 prop_dictionary_copy_mutable(prop_dictionary_t opd)
619 {
620 prop_dictionary_t pd;
621
622 if (! prop_object_is_dictionary(opd))
623 return (NULL);
624
625 pd = prop_dictionary_copy(opd);
626 if (pd != NULL)
627 pd->pd_flags &= ~PD_F_IMMUTABLE;
628
629 return (pd);
630 }
631
632 /*
633 * prop_dictionary_count --
634 * Return the number of objects stored in the dictionary.
635 */
636 unsigned int
637 prop_dictionary_count(prop_dictionary_t pd)
638 {
639 unsigned int rv;
640
641 if (! prop_object_is_dictionary(pd))
642 return (0);
643
644 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
645 rv = pd->pd_count;
646 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
647
648 return (rv);
649 }
650
651 /*
652 * prop_dictionary_ensure_capacity --
653 * Ensure that the dictionary has the capacity to store the specified
654 * total number of objects (including the objects already stored in
655 * the dictionary).
656 */
657 boolean_t
658 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
659 {
660 boolean_t rv;
661
662 if (! prop_object_is_dictionary(pd))
663 return (FALSE);
664
665 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
666 if (capacity > pd->pd_capacity)
667 rv = _prop_dictionary_expand(pd, capacity);
668 else
669 rv = TRUE;
670 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
671 return (rv);
672 }
673
674 /*
675 * prop_dictionary_iterator --
676 * Return an iterator for the dictionary. The dictionary is retained by
677 * the iterator.
678 */
679 prop_object_iterator_t
680 prop_dictionary_iterator(prop_dictionary_t pd)
681 {
682 struct _prop_dictionary_iterator *pdi;
683
684 if (! prop_object_is_dictionary(pd))
685 return (NULL);
686
687 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
688 if (pdi == NULL)
689 return (NULL);
690 pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
691 pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
692 prop_object_retain(pd);
693 pdi->pdi_base.pi_obj = pd;
694 _prop_dictionary_iterator_reset(pdi);
695
696 return (&pdi->pdi_base);
697 }
698
699 static struct _prop_dict_entry *
700 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
701 unsigned int *idxp)
702 {
703 struct _prop_dict_entry *pde;
704 unsigned int base, idx, distance;
705 int res;
706
707 /*
708 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
709 */
710
711 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
712 distance >>= 1) {
713 idx = base + (distance >> 1);
714 pde = &pd->pd_array[idx];
715 _PROP_ASSERT(pde->pde_key != NULL);
716 res = strcmp(key, pde->pde_key->pdk_key);
717 if (res == 0) {
718 if (idxp != NULL)
719 *idxp = idx;
720 return (pde);
721 }
722 if (res > 0) { /* key > pdk_key: move right */
723 base = idx + 1;
724 distance--;
725 } /* else move left */
726 }
727
728 /* idx points to the slot we looked at last. */
729 if (idxp != NULL)
730 *idxp = idx;
731 return (NULL);
732 }
733
734 /*
735 * prop_dictionary_get --
736 * Return the object stored with specified key.
737 */
738 prop_object_t
739 prop_dictionary_get(prop_dictionary_t pd, const char *key)
740 {
741 const struct _prop_dict_entry *pde;
742 prop_object_t po = NULL;
743
744 if (! prop_object_is_dictionary(pd))
745 return (NULL);
746
747 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
748 pde = _prop_dict_lookup(pd, key, NULL);
749 if (pde != NULL) {
750 _PROP_ASSERT(pde->pde_objref != NULL);
751 po = pde->pde_objref;
752 }
753 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
754 return (po);
755 }
756
757 /*
758 * prop_dictionary_get_keysym --
759 * Return the object stored at the location encoded by the keysym.
760 */
761 prop_object_t
762 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
763 {
764
765 if (! (prop_object_is_dictionary(pd) &&
766 prop_object_is_dictionary_keysym(pdk)))
767 return (NULL);
768
769 return (prop_dictionary_get(pd, pdk->pdk_key));
770 }
771
772 /*
773 * prop_dictionary_set --
774 * Store a reference to an object at with the specified key.
775 * If the key already exisit, the original object is released.
776 */
777 boolean_t
778 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
779 {
780 struct _prop_dict_entry *pde;
781 prop_dictionary_keysym_t pdk;
782 unsigned int idx;
783 boolean_t rv = FALSE;
784
785 if (! prop_object_is_dictionary(pd))
786 return (FALSE);
787
788 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
789
790 if (prop_dictionary_is_immutable(pd))
791 return (FALSE);
792
793 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
794
795 pde = _prop_dict_lookup(pd, key, &idx);
796 if (pde != NULL) {
797 prop_object_t opo = pde->pde_objref;
798 prop_object_retain(po);
799 pde->pde_objref = po;
800 prop_object_release(opo);
801 rv = TRUE;
802 goto out;
803 }
804
805 pdk = _prop_dict_keysym_alloc(key);
806 if (pdk == NULL)
807 goto out;
808
809 if (pd->pd_count == pd->pd_capacity &&
810 _prop_dictionary_expand(pd,
811 pd->pd_capacity + EXPAND_STEP) == FALSE) {
812 prop_object_release(pdk);
813 goto out;
814 }
815
816 /* At this point, the store will succeed. */
817 prop_object_retain(po);
818
819 if (pd->pd_count == 0) {
820 pd->pd_array[0].pde_key = pdk;
821 pd->pd_array[0].pde_objref = po;
822 pd->pd_count++;
823 pd->pd_version++;
824 rv = TRUE;
825 goto out;
826 }
827
828 pde = &pd->pd_array[idx];
829 _PROP_ASSERT(pde->pde_key != NULL);
830
831 if (strcmp(key, pde->pde_key->pdk_key) < 0) {
832 /*
833 * key < pdk_key: insert to the left. This is the same as
834 * inserting to the right, except we decrement the current
835 * index first.
836 *
837 * Because we're unsigned, we have to special case 0
838 * (grumble).
839 */
840 if (idx == 0) {
841 memmove(&pd->pd_array[1], &pd->pd_array[0],
842 pd->pd_count * sizeof(*pde));
843 pd->pd_array[0].pde_key = pdk;
844 pd->pd_array[0].pde_objref = po;
845 pd->pd_count++;
846 pd->pd_version++;
847 rv = TRUE;
848 goto out;
849 }
850 idx--;
851 }
852
853 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
854 (pd->pd_count - (idx + 1)) * sizeof(*pde));
855 pd->pd_array[idx + 1].pde_key = pdk;
856 pd->pd_array[idx + 1].pde_objref = po;
857 pd->pd_count++;
858
859 pd->pd_version++;
860
861 rv = TRUE;
862
863 out:
864 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
865 return (rv);
866 }
867
868 /*
869 * prop_dictionary_set_keysym --
870 * Replace the object in the dictionary at the location encoded by
871 * the keysym.
872 */
873 boolean_t
874 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
875 prop_object_t po)
876 {
877
878 if (! (prop_object_is_dictionary(pd) &&
879 prop_object_is_dictionary_keysym(pdk)))
880 return (FALSE);
881
882 return (prop_dictionary_set(pd, pdk->pdk_key, po));
883 }
884
885 static void
886 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
887 unsigned int idx)
888 {
889 prop_dictionary_keysym_t pdk = pde->pde_key;
890 prop_object_t po = pde->pde_objref;
891
892 /*
893 * Dictionary must be WRITE-LOCKED.
894 */
895
896 _PROP_ASSERT(pd->pd_count != 0);
897 _PROP_ASSERT(idx < pd->pd_count);
898 _PROP_ASSERT(pde == &pd->pd_array[idx]);
899
900 idx++;
901 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
902 (pd->pd_count - idx) * sizeof(*pde));
903 pd->pd_count--;
904 pd->pd_version++;
905
906 prop_object_release(pdk);
907 prop_object_release(po);
908 }
909
910 /*
911 * prop_dictionary_remove --
912 * Remove the reference to an object with the specified key from
913 * the dictionary.
914 */
915 void
916 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
917 {
918 struct _prop_dict_entry *pde;
919 unsigned int idx;
920
921 if (! prop_object_is_dictionary(pd))
922 return;
923
924 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
925
926 /* XXX Should this be a _PROP_ASSERT()? */
927 if (prop_dictionary_is_immutable(pd))
928 goto out;
929
930 pde = _prop_dict_lookup(pd, key, &idx);
931 /* XXX Should this be a _PROP_ASSERT()? */
932 if (pde == NULL)
933 goto out;
934
935 _prop_dictionary_remove(pd, pde, idx);
936 out:
937 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
938 }
939
940 /*
941 * prop_dictionary_remove_keysym --
942 * Remove a reference to an object stored in the dictionary at the
943 * location encoded by the keysym.
944 */
945 void
946 prop_dictionary_remove_keysym(prop_dictionary_t pd,
947 prop_dictionary_keysym_t pdk)
948 {
949
950 if (! (prop_object_is_dictionary(pd) &&
951 prop_object_is_dictionary_keysym(pdk)))
952 return;
953
954 prop_dictionary_remove(pd, pdk->pdk_key);
955 }
956
957 /*
958 * prop_dictionary_equals --
959 * Return TRUE if the two dictionaries are equivalent. Note we do a
960 * by-value comparison of the objects in the dictionary.
961 */
962 boolean_t
963 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
964 {
965
966 return (_prop_dictionary_equals(dict1, dict2));
967 }
968
969 /*
970 * prop_dictionary_keysym_cstring_nocopy --
971 * Return an immutable reference to the keysym's value.
972 */
973 const char *
974 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
975 {
976
977 if (! prop_object_is_dictionary_keysym(pdk))
978 return (NULL);
979
980 return (pdk->pdk_key);
981 }
982
983 /*
984 * prop_dictionary_keysym_equals --
985 * Return TRUE if the two dictionary key symbols are equivalent.
986 * Note: We do not compare the object references.
987 */
988 boolean_t
989 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
990 prop_dictionary_keysym_t pdk2)
991 {
992
993 return (_prop_dict_keysym_equals(pdk1, pdk2));
994 }
995
996 /*
997 * prop_dictionary_externalize --
998 * Externalize a dictionary, returning a NUL-terminated buffer
999 * containing the XML-style representation. The buffer is allocated
1000 * with the M_TEMP memory type.
1001 */
1002 char *
1003 prop_dictionary_externalize(prop_dictionary_t pd)
1004 {
1005 struct _prop_object_externalize_context *ctx;
1006 char *cp;
1007
1008 ctx = _prop_object_externalize_context_alloc();
1009 if (ctx == NULL)
1010 return (NULL);
1011
1012 if (_prop_object_externalize_header(ctx) == FALSE ||
1013 (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == FALSE ||
1014 _prop_object_externalize_footer(ctx) == FALSE) {
1015 /* We are responsible for releasing the buffer. */
1016 _PROP_FREE(ctx->poec_buf, M_TEMP);
1017 _prop_object_externalize_context_free(ctx);
1018 return (NULL);
1019 }
1020
1021 cp = ctx->poec_buf;
1022 _prop_object_externalize_context_free(ctx);
1023
1024 return (cp);
1025 }
1026
1027 /*
1028 * _prop_dictionary_internalize --
1029 * Parse a <dict>...</dict> and return the object created from the
1030 * external representation.
1031 */
1032 prop_object_t
1033 _prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
1034 {
1035 prop_dictionary_t dict;
1036 prop_object_t val;
1037 size_t keylen;
1038 char *tmpkey;
1039
1040 /* We don't currently understand any attributes. */
1041 if (ctx->poic_tagattr != NULL)
1042 return (NULL);
1043
1044 dict = prop_dictionary_create();
1045 if (dict == NULL)
1046 return (NULL);
1047
1048 if (ctx->poic_is_empty_element)
1049 return (dict);
1050
1051 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1052 if (tmpkey == NULL)
1053 goto bad;
1054
1055 for (;;) {
1056 /* Fetch the next tag. */
1057 if (_prop_object_internalize_find_tag(ctx, NULL,
1058 _PROP_TAG_TYPE_EITHER) == FALSE)
1059 goto bad;
1060
1061 /* Check to see if this is the end of the dictionary. */
1062 if (_PROP_TAG_MATCH(ctx, "dict") &&
1063 ctx->poic_tag_type == _PROP_TAG_TYPE_END)
1064 break;
1065
1066 /* Ok, it must be a non-empty key start tag. */
1067 if (!_PROP_TAG_MATCH(ctx, "key") ||
1068 ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
1069 ctx->poic_is_empty_element)
1070 goto bad;
1071
1072 if (_prop_object_internalize_decode_string(ctx,
1073 tmpkey, PDK_MAXKEY, &keylen,
1074 &ctx->poic_cp) == FALSE)
1075 goto bad;
1076
1077 _PROP_ASSERT(keylen <= PDK_MAXKEY);
1078 tmpkey[keylen] = '\0';
1079
1080 if (_prop_object_internalize_find_tag(ctx, "key",
1081 _PROP_TAG_TYPE_END) == FALSE)
1082 goto bad;
1083
1084 /* ..and now the beginning of the value. */
1085 if (_prop_object_internalize_find_tag(ctx, NULL,
1086 _PROP_TAG_TYPE_START) == FALSE)
1087 goto bad;
1088
1089 val = _prop_object_internalize_by_tag(ctx);
1090 if (val == NULL)
1091 goto bad;
1092
1093 if (prop_dictionary_set(dict, tmpkey, val) == FALSE) {
1094 prop_object_release(val);
1095 goto bad;
1096 }
1097 prop_object_release(val);
1098 }
1099
1100 _PROP_FREE(tmpkey, M_TEMP);
1101 return (dict);
1102
1103 bad:
1104 if (tmpkey != NULL)
1105 _PROP_FREE(tmpkey, M_TEMP);
1106 prop_object_release(dict);
1107 return (NULL);
1108 }
1109
1110 /*
1111 * prop_dictionary_internalize --
1112 * Create a dictionary by parsing the NUL-terminated XML-style
1113 * representation.
1114 */
1115 prop_dictionary_t
1116 prop_dictionary_internalize(const char *xml)
1117 {
1118 prop_dictionary_t dict = NULL;
1119 struct _prop_object_internalize_context *ctx;
1120
1121 ctx = _prop_object_internalize_context_alloc(xml);
1122 if (ctx == NULL)
1123 return (NULL);
1124
1125 /* We start with a <plist> tag. */
1126 if (_prop_object_internalize_find_tag(ctx, "plist",
1127 _PROP_TAG_TYPE_START) == FALSE)
1128 goto out;
1129
1130 /* Plist elements cannot be empty. */
1131 if (ctx->poic_is_empty_element)
1132 goto out;
1133
1134 /*
1135 * We don't understand any plist attributes, but Apple XML
1136 * property lists often have a "version" attribute. If we
1137 * see that one, we simply ignore it.
1138 */
1139 if (ctx->poic_tagattr != NULL &&
1140 !_PROP_TAGATTR_MATCH(ctx, "version"))
1141 goto out;
1142
1143 /* Next we expect to see <dict>. */
1144 if (_prop_object_internalize_find_tag(ctx, "dict",
1145 _PROP_TAG_TYPE_START) == FALSE)
1146 goto out;
1147
1148 dict = _prop_dictionary_internalize(ctx);
1149 if (dict == NULL)
1150 goto out;
1151
1152 /* We've advanced past </dict>. Now we want </plist>. */
1153 if (_prop_object_internalize_find_tag(ctx, "plist",
1154 _PROP_TAG_TYPE_END) == FALSE) {
1155 prop_object_release(dict);
1156 dict = NULL;
1157 }
1158
1159 out:
1160 _prop_object_internalize_context_free(ctx);
1161 return (dict);
1162 }
1163
1164 #if !defined(_KERNEL) && !defined(_STANDALONE)
1165 /*
1166 * prop_dictionary_externalize_to_file --
1167 * Externalize a dictionary to the specified file.
1168 */
1169 boolean_t
1170 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1171 {
1172 char *xml;
1173 boolean_t rv;
1174 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */
1175
1176 xml = prop_dictionary_externalize(dict);
1177 if (xml == NULL)
1178 return (FALSE);
1179 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
1180 if (rv == FALSE)
1181 save_errno = errno;
1182 _PROP_FREE(xml, M_TEMP);
1183 if (rv == FALSE)
1184 errno = save_errno;
1185
1186 return (rv);
1187 }
1188
1189 /*
1190 * prop_dictionary_internalize_from_file --
1191 * Internalize a dictionary from a file.
1192 */
1193 prop_dictionary_t
1194 prop_dictionary_internalize_from_file(const char *fname)
1195 {
1196 struct _prop_object_internalize_mapped_file *mf;
1197 prop_dictionary_t dict;
1198
1199 mf = _prop_object_internalize_map_file(fname);
1200 if (mf == NULL)
1201 return (NULL);
1202 dict = prop_dictionary_internalize(mf->poimf_xml);
1203 _prop_object_internalize_unmap_file(mf);
1204
1205 return (dict);
1206 }
1207 #endif /* !_KERNEL && !_STANDALONE */
1208