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