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