prop_dictionary.c revision 1.25 1 /* $NetBSD: prop_dictionary.c,v 1.25 2008/05/06 13:52:51 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
403 if (pd->pd_count == 0) {
404 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
405 return (_prop_object_externalize_empty_tag(ctx, "dict"));
406 }
407
408 if (_prop_object_externalize_start_tag(ctx, "dict") == false ||
409 _prop_object_externalize_append_char(ctx, '\n') == false)
410 goto out;
411
412 pi = prop_dictionary_iterator(pd);
413 if (pi == NULL)
414 goto out;
415
416 ctx->poec_depth++;
417 _PROP_ASSERT(ctx->poec_depth != 0);
418
419 while ((pdk = prop_object_iterator_next(pi)) != NULL) {
420 po = prop_dictionary_get_keysym(pd, pdk);
421 if (po == NULL ||
422 _prop_object_externalize_start_tag(ctx, "key") == false ||
423 _prop_object_externalize_append_encoded_cstring(ctx,
424 pdk->pdk_key) == false ||
425 _prop_object_externalize_end_tag(ctx, "key") == false ||
426 (*po->po_type->pot_extern)(ctx, po) == false) {
427 prop_object_iterator_release(pi);
428 goto out;
429 }
430 }
431
432 prop_object_iterator_release(pi);
433
434 ctx->poec_depth--;
435 for (i = 0; i < ctx->poec_depth; i++) {
436 if (_prop_object_externalize_append_char(ctx, '\t') == false)
437 goto out;
438 }
439 if (_prop_object_externalize_end_tag(ctx, "dict") == false)
440 goto out;
441
442 rv = true;
443
444 out:
445 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
446 return (rv);
447 }
448
449 /* ARGSUSED */
450 static bool
451 _prop_dictionary_equals(prop_object_t v1, prop_object_t v2,
452 void **stored_pointer1, void **stored_pointer2,
453 prop_object_t *next_obj1, prop_object_t *next_obj2)
454 {
455 prop_dictionary_t dict1 = v1;
456 prop_dictionary_t dict2 = v2;
457 uintptr_t idx;
458 bool rv = _PROP_OBJECT_EQUALS_FALSE;
459
460 if (dict1 == dict2)
461 return (_PROP_OBJECT_EQUALS_TRUE);
462
463 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
464
465 idx = (uintptr_t)*stored_pointer1;
466
467 if (idx == 0) {
468 if ((uintptr_t)dict1 < (uintptr_t)dict2) {
469 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
470 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
471 } else {
472 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
473 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
474 }
475 }
476
477 if (dict1->pd_count != dict2->pd_count)
478 goto out;
479
480 if (idx == dict1->pd_count) {
481 rv = _PROP_OBJECT_EQUALS_TRUE;
482 goto out;
483 }
484
485 _PROP_ASSERT(idx < dict1->pd_count);
486
487 *stored_pointer1 = (void *)(idx + 1);
488 *stored_pointer2 = (void *)(idx + 1);
489
490 *next_obj1 = &dict1->pd_array[idx].pde_objref;
491 *next_obj2 = &dict2->pd_array[idx].pde_objref;
492
493 if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key,
494 dict2->pd_array[idx].pde_key))
495 goto out;
496
497 return (_PROP_OBJECT_EQUALS_RECURSE);
498
499 out:
500 _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
501 _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
502 return (rv);
503 }
504
505 static void
506 _prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2)
507 {
508 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock);
509 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock);
510 }
511
512 static prop_dictionary_t
513 _prop_dictionary_alloc(unsigned int capacity)
514 {
515 prop_dictionary_t pd;
516 struct _prop_dict_entry *array;
517
518 if (capacity != 0) {
519 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
520 if (array == NULL)
521 return (NULL);
522 } else
523 array = NULL;
524
525 pd = _PROP_POOL_GET(_prop_dictionary_pool);
526 if (pd != NULL) {
527 _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
528
529 _PROP_RWLOCK_INIT(pd->pd_rwlock);
530 pd->pd_array = array;
531 pd->pd_capacity = capacity;
532 pd->pd_count = 0;
533 pd->pd_flags = 0;
534
535 pd->pd_version = 0;
536 } else if (array != NULL)
537 _PROP_FREE(array, M_PROP_DICT);
538
539 return (pd);
540 }
541
542 static bool
543 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
544 {
545 struct _prop_dict_entry *array, *oarray;
546
547 /*
548 * Dictionary must be WRITE-LOCKED.
549 */
550
551 oarray = pd->pd_array;
552
553 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
554 if (array == NULL)
555 return (false);
556 if (oarray != NULL)
557 memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
558 pd->pd_array = array;
559 pd->pd_capacity = capacity;
560
561 if (oarray != NULL)
562 _PROP_FREE(oarray, M_PROP_DICT);
563
564 return (true);
565 }
566
567 static prop_object_t
568 _prop_dictionary_iterator_next_object(void *v)
569 {
570 struct _prop_dictionary_iterator *pdi = v;
571 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
572 prop_dictionary_keysym_t pdk = NULL;
573
574 _PROP_ASSERT(prop_object_is_dictionary(pd));
575
576 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
577
578 if (pd->pd_version != pdi->pdi_base.pi_version)
579 goto out; /* dictionary changed during iteration */
580
581 _PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
582
583 if (pdi->pdi_index == pd->pd_count)
584 goto out; /* we've iterated all objects */
585
586 pdk = pd->pd_array[pdi->pdi_index].pde_key;
587 pdi->pdi_index++;
588
589 out:
590 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
591 return (pdk);
592 }
593
594 static void
595 _prop_dictionary_iterator_reset(void *v)
596 {
597 struct _prop_dictionary_iterator *pdi = v;
598 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
599
600 _PROP_ASSERT(prop_object_is_dictionary(pd));
601 _PROP_RWLOCK_OWNED(pd->pd_rwlock);
602
603 pdi->pdi_index = 0;
604 pdi->pdi_base.pi_version = pd->pd_version;
605 }
606
607 /*
608 * prop_dictionary_create --
609 * Create a dictionary.
610 */
611 prop_dictionary_t
612 prop_dictionary_create(void)
613 {
614
615 return (_prop_dictionary_alloc(0));
616 }
617
618 /*
619 * prop_dictionary_create_with_capacity --
620 * Create a dictionary with the capacity to store N objects.
621 */
622 prop_dictionary_t
623 prop_dictionary_create_with_capacity(unsigned int capacity)
624 {
625
626 return (_prop_dictionary_alloc(capacity));
627 }
628
629 /*
630 * prop_dictionary_copy --
631 * Copy a dictionary. The new dictionary has an initial capacity equal
632 * to the number of objects stored int the original dictionary. The new
633 * dictionary contains refrences to the original dictionary's objects,
634 * not copies of those objects (i.e. a shallow copy).
635 */
636 prop_dictionary_t
637 prop_dictionary_copy(prop_dictionary_t opd)
638 {
639 prop_dictionary_t pd;
640 prop_dictionary_keysym_t pdk;
641 prop_object_t po;
642 unsigned int idx;
643
644 if (! prop_object_is_dictionary(opd))
645 return (NULL);
646
647 _PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
648
649 pd = _prop_dictionary_alloc(opd->pd_count);
650 if (pd != NULL) {
651 for (idx = 0; idx < opd->pd_count; idx++) {
652 pdk = opd->pd_array[idx].pde_key;
653 po = opd->pd_array[idx].pde_objref;
654
655 prop_object_retain(pdk);
656 prop_object_retain(po);
657
658 pd->pd_array[idx].pde_key = pdk;
659 pd->pd_array[idx].pde_objref = po;
660 }
661 pd->pd_count = opd->pd_count;
662 pd->pd_flags = opd->pd_flags;
663 }
664 _PROP_RWLOCK_UNLOCK(opd->pd_rwlock);
665 return (pd);
666 }
667
668 /*
669 * prop_dictionary_copy_mutable --
670 * Like prop_dictionary_copy(), but the resulting dictionary is
671 * mutable.
672 */
673 prop_dictionary_t
674 prop_dictionary_copy_mutable(prop_dictionary_t opd)
675 {
676 prop_dictionary_t pd;
677
678 if (! prop_object_is_dictionary(opd))
679 return (NULL);
680
681 pd = prop_dictionary_copy(opd);
682 if (pd != NULL)
683 pd->pd_flags &= ~PD_F_IMMUTABLE;
684
685 return (pd);
686 }
687
688 /*
689 * prop_dictionary_make_immutable --
690 * Set the immutable flag on that dictionary.
691 */
692 void
693 prop_dictionary_make_immutable(prop_dictionary_t pd)
694 {
695
696 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
697 if (prop_dictionary_is_immutable(pd) == false)
698 pd->pd_flags |= PD_F_IMMUTABLE;
699 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
700 }
701
702 /*
703 * prop_dictionary_count --
704 * Return the number of objects stored in the dictionary.
705 */
706 unsigned int
707 prop_dictionary_count(prop_dictionary_t pd)
708 {
709 unsigned int rv;
710
711 if (! prop_object_is_dictionary(pd))
712 return (0);
713
714 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
715 rv = pd->pd_count;
716 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
717
718 return (rv);
719 }
720
721 /*
722 * prop_dictionary_ensure_capacity --
723 * Ensure that the dictionary has the capacity to store the specified
724 * total number of objects (including the objects already stored in
725 * the dictionary).
726 */
727 bool
728 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
729 {
730 bool rv;
731
732 if (! prop_object_is_dictionary(pd))
733 return (false);
734
735 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
736 if (capacity > pd->pd_capacity)
737 rv = _prop_dictionary_expand(pd, capacity);
738 else
739 rv = true;
740 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
741 return (rv);
742 }
743
744 /*
745 * prop_dictionary_iterator --
746 * Return an iterator for the dictionary. The dictionary is retained by
747 * the iterator.
748 */
749 prop_object_iterator_t
750 prop_dictionary_iterator(prop_dictionary_t pd)
751 {
752 struct _prop_dictionary_iterator *pdi;
753
754 if (! prop_object_is_dictionary(pd))
755 return (NULL);
756
757 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
758 if (pdi == NULL)
759 return (NULL);
760 pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
761 pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
762 prop_object_retain(pd);
763 pdi->pdi_base.pi_obj = pd;
764 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
765 _prop_dictionary_iterator_reset(pdi);
766 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
767
768 return (&pdi->pdi_base);
769 }
770
771 /*
772 * prop_dictionary_all_keys --
773 * Return an array containing a snapshot of all of the keys
774 * in the dictionary.
775 */
776 prop_array_t
777 prop_dictionary_all_keys(prop_dictionary_t pd)
778 {
779 prop_array_t array;
780 unsigned int idx;
781 bool rv = true;
782
783 if (! prop_object_is_dictionary(pd))
784 return (NULL);
785
786 /* There is no pressing need to lock the dictionary for this. */
787 array = prop_array_create_with_capacity(pd->pd_count);
788
789 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
790
791 for (idx = 0; idx < pd->pd_count; idx++) {
792 rv = prop_array_add(array, pd->pd_array[idx].pde_key);
793 if (rv == false)
794 break;
795 }
796
797 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
798
799 if (rv == false) {
800 prop_object_release(array);
801 array = NULL;
802 }
803 return (array);
804 }
805
806 static struct _prop_dict_entry *
807 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
808 unsigned int *idxp)
809 {
810 struct _prop_dict_entry *pde;
811 unsigned int base, idx, distance;
812 int res;
813
814 /*
815 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
816 */
817
818 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
819 distance >>= 1) {
820 idx = base + (distance >> 1);
821 pde = &pd->pd_array[idx];
822 _PROP_ASSERT(pde->pde_key != NULL);
823 res = strcmp(key, pde->pde_key->pdk_key);
824 if (res == 0) {
825 if (idxp != NULL)
826 *idxp = idx;
827 return (pde);
828 }
829 if (res > 0) { /* key > pdk_key: move right */
830 base = idx + 1;
831 distance--;
832 } /* else move left */
833 }
834
835 /* idx points to the slot we looked at last. */
836 if (idxp != NULL)
837 *idxp = idx;
838 return (NULL);
839 }
840
841 /*
842 * prop_dictionary_get --
843 * Return the object stored with specified key.
844 */
845 prop_object_t
846 prop_dictionary_get(prop_dictionary_t pd, const char *key)
847 {
848 const struct _prop_dict_entry *pde;
849 prop_object_t po = NULL;
850
851 if (! prop_object_is_dictionary(pd))
852 return (NULL);
853
854 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
855 pde = _prop_dict_lookup(pd, key, NULL);
856 if (pde != NULL) {
857 _PROP_ASSERT(pde->pde_objref != NULL);
858 po = pde->pde_objref;
859 }
860 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
861 return (po);
862 }
863
864 /*
865 * prop_dictionary_get_keysym --
866 * Return the object stored at the location encoded by the keysym.
867 */
868 prop_object_t
869 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
870 {
871
872 if (! (prop_object_is_dictionary(pd) &&
873 prop_object_is_dictionary_keysym(pdk)))
874 return (NULL);
875
876 return (prop_dictionary_get(pd, pdk->pdk_key));
877 }
878
879 /*
880 * prop_dictionary_set --
881 * Store a reference to an object at with the specified key.
882 * If the key already exisit, the original object is released.
883 */
884 bool
885 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
886 {
887 struct _prop_dict_entry *pde;
888 prop_dictionary_keysym_t pdk;
889 unsigned int idx;
890 bool rv = false;
891
892 if (! prop_object_is_dictionary(pd))
893 return (false);
894
895 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
896
897 if (prop_dictionary_is_immutable(pd))
898 return (false);
899
900 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
901
902 pde = _prop_dict_lookup(pd, key, &idx);
903 if (pde != NULL) {
904 prop_object_t opo = pde->pde_objref;
905 prop_object_retain(po);
906 pde->pde_objref = po;
907 prop_object_release(opo);
908 rv = true;
909 goto out;
910 }
911
912 pdk = _prop_dict_keysym_alloc(key);
913 if (pdk == NULL)
914 goto out;
915
916 if (pd->pd_count == pd->pd_capacity &&
917 _prop_dictionary_expand(pd,
918 pd->pd_capacity + EXPAND_STEP) == false) {
919 prop_object_release(pdk);
920 goto out;
921 }
922
923 /* At this point, the store will succeed. */
924 prop_object_retain(po);
925
926 if (pd->pd_count == 0) {
927 pd->pd_array[0].pde_key = pdk;
928 pd->pd_array[0].pde_objref = po;
929 pd->pd_count++;
930 pd->pd_version++;
931 rv = true;
932 goto out;
933 }
934
935 pde = &pd->pd_array[idx];
936 _PROP_ASSERT(pde->pde_key != NULL);
937
938 if (strcmp(key, pde->pde_key->pdk_key) < 0) {
939 /*
940 * key < pdk_key: insert to the left. This is the same as
941 * inserting to the right, except we decrement the current
942 * index first.
943 *
944 * Because we're unsigned, we have to special case 0
945 * (grumble).
946 */
947 if (idx == 0) {
948 memmove(&pd->pd_array[1], &pd->pd_array[0],
949 pd->pd_count * sizeof(*pde));
950 pd->pd_array[0].pde_key = pdk;
951 pd->pd_array[0].pde_objref = po;
952 pd->pd_count++;
953 pd->pd_version++;
954 rv = true;
955 goto out;
956 }
957 idx--;
958 }
959
960 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
961 (pd->pd_count - (idx + 1)) * sizeof(*pde));
962 pd->pd_array[idx + 1].pde_key = pdk;
963 pd->pd_array[idx + 1].pde_objref = po;
964 pd->pd_count++;
965
966 pd->pd_version++;
967
968 rv = true;
969
970 out:
971 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
972 return (rv);
973 }
974
975 /*
976 * prop_dictionary_set_keysym --
977 * Replace the object in the dictionary at the location encoded by
978 * the keysym.
979 */
980 bool
981 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
982 prop_object_t po)
983 {
984
985 if (! (prop_object_is_dictionary(pd) &&
986 prop_object_is_dictionary_keysym(pdk)))
987 return (false);
988
989 return (prop_dictionary_set(pd, pdk->pdk_key, po));
990 }
991
992 static void
993 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
994 unsigned int idx)
995 {
996 prop_dictionary_keysym_t pdk = pde->pde_key;
997 prop_object_t po = pde->pde_objref;
998
999 /*
1000 * Dictionary must be WRITE-LOCKED.
1001 */
1002
1003 _PROP_ASSERT(pd->pd_count != 0);
1004 _PROP_ASSERT(idx < pd->pd_count);
1005 _PROP_ASSERT(pde == &pd->pd_array[idx]);
1006
1007 idx++;
1008 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
1009 (pd->pd_count - idx) * sizeof(*pde));
1010 pd->pd_count--;
1011 pd->pd_version++;
1012
1013 prop_object_release(pdk);
1014 prop_object_release(po);
1015 }
1016
1017 /*
1018 * prop_dictionary_remove --
1019 * Remove the reference to an object with the specified key from
1020 * the dictionary.
1021 */
1022 void
1023 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
1024 {
1025 struct _prop_dict_entry *pde;
1026 unsigned int idx;
1027
1028 if (! prop_object_is_dictionary(pd))
1029 return;
1030
1031 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1032
1033 /* XXX Should this be a _PROP_ASSERT()? */
1034 if (prop_dictionary_is_immutable(pd))
1035 goto out;
1036
1037 pde = _prop_dict_lookup(pd, key, &idx);
1038 /* XXX Should this be a _PROP_ASSERT()? */
1039 if (pde == NULL)
1040 goto out;
1041
1042 _prop_dictionary_remove(pd, pde, idx);
1043 out:
1044 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1045 }
1046
1047 /*
1048 * prop_dictionary_remove_keysym --
1049 * Remove a reference to an object stored in the dictionary at the
1050 * location encoded by the keysym.
1051 */
1052 void
1053 prop_dictionary_remove_keysym(prop_dictionary_t pd,
1054 prop_dictionary_keysym_t pdk)
1055 {
1056
1057 if (! (prop_object_is_dictionary(pd) &&
1058 prop_object_is_dictionary_keysym(pdk)))
1059 return;
1060
1061 prop_dictionary_remove(pd, pdk->pdk_key);
1062 }
1063
1064 /*
1065 * prop_dictionary_equals --
1066 * Return true if the two dictionaries are equivalent. Note we do a
1067 * by-value comparison of the objects in the dictionary.
1068 */
1069 bool
1070 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
1071 {
1072 if (!prop_object_is_dictionary(dict1) ||
1073 !prop_object_is_dictionary(dict2))
1074 return (false);
1075
1076 return (prop_object_equals(dict1, dict2));
1077 }
1078
1079 /*
1080 * prop_dictionary_keysym_cstring_nocopy --
1081 * Return an immutable reference to the keysym's value.
1082 */
1083 const char *
1084 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
1085 {
1086
1087 if (! prop_object_is_dictionary_keysym(pdk))
1088 return (NULL);
1089
1090 return (pdk->pdk_key);
1091 }
1092
1093 /*
1094 * prop_dictionary_keysym_equals --
1095 * Return true if the two dictionary key symbols are equivalent.
1096 * Note: We do not compare the object references.
1097 */
1098 bool
1099 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
1100 prop_dictionary_keysym_t pdk2)
1101 {
1102 if (!prop_object_is_dictionary_keysym(pdk1) ||
1103 !prop_object_is_dictionary_keysym(pdk2))
1104 return (_PROP_OBJECT_EQUALS_FALSE);
1105
1106 return (prop_object_equals(pdk1, pdk2));
1107 }
1108
1109 /*
1110 * prop_dictionary_externalize --
1111 * Externalize a dictionary, returning a NUL-terminated buffer
1112 * containing the XML-style representation. The buffer is allocated
1113 * with the M_TEMP memory type.
1114 */
1115 char *
1116 prop_dictionary_externalize(prop_dictionary_t pd)
1117 {
1118 struct _prop_object_externalize_context *ctx;
1119 char *cp;
1120
1121 ctx = _prop_object_externalize_context_alloc();
1122 if (ctx == NULL)
1123 return (NULL);
1124
1125 if (_prop_object_externalize_header(ctx) == false ||
1126 (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false ||
1127 _prop_object_externalize_footer(ctx) == false) {
1128 /* We are responsible for releasing the buffer. */
1129 _PROP_FREE(ctx->poec_buf, M_TEMP);
1130 _prop_object_externalize_context_free(ctx);
1131 return (NULL);
1132 }
1133
1134 cp = ctx->poec_buf;
1135 _prop_object_externalize_context_free(ctx);
1136
1137 return (cp);
1138 }
1139
1140 /*
1141 * _prop_dictionary_internalize --
1142 * Parse a <dict>...</dict> and return the object created from the
1143 * external representation.
1144 *
1145 * Internal state in via rec_data is the storage area for the last processed
1146 * key.
1147 * _prop_dictionary_internalize_body is the upper half of the parse loop.
1148 * It is responsible for parsing the key directly and storing it in the area
1149 * referenced by rec_data.
1150 * _prop_dictionary_internalize_cont is the lower half and called with the value
1151 * associated with the key.
1152 */
1153 static bool _prop_dictionary_internalize_body(prop_stack_t,
1154 prop_object_t *, struct _prop_object_internalize_context *, char *);
1155
1156 bool
1157 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
1158 struct _prop_object_internalize_context *ctx)
1159 {
1160 prop_dictionary_t dict;
1161 char *tmpkey;
1162
1163 /* We don't currently understand any attributes. */
1164 if (ctx->poic_tagattr != NULL)
1165 return (true);
1166
1167 dict = prop_dictionary_create();
1168 if (dict == NULL)
1169 return (true);
1170
1171 if (ctx->poic_is_empty_element) {
1172 *obj = dict;
1173 return (true);
1174 }
1175
1176 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1177 if (tmpkey == NULL) {
1178 prop_object_release(dict);
1179 return (true);
1180 }
1181
1182 *obj = dict;
1183 /*
1184 * Opening tag is found, storage for key allocated and
1185 * now continue to the first element.
1186 */
1187 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1188 }
1189
1190 static bool
1191 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
1192 struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
1193 {
1194 prop_dictionary_t dict = *obj;
1195 char *tmpkey = data;
1196
1197 _PROP_ASSERT(tmpkey != NULL);
1198
1199 if (child == NULL ||
1200 prop_dictionary_set(dict, tmpkey, child) == false) {
1201 _PROP_FREE(tmpkey, M_TEMP);
1202 if (child != NULL)
1203 prop_object_release(child);
1204 prop_object_release(dict);
1205 *obj = NULL;
1206 return (true);
1207 }
1208
1209 prop_object_release(child);
1210
1211 /*
1212 * key, value was added, now continue looking for the next key
1213 * or the closing tag.
1214 */
1215 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1216 }
1217
1218 static bool
1219 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
1220 struct _prop_object_internalize_context *ctx, char *tmpkey)
1221 {
1222 prop_dictionary_t dict = *obj;
1223 size_t keylen;
1224
1225 /* Fetch the next tag. */
1226 if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
1227 goto bad;
1228
1229 /* Check to see if this is the end of the dictionary. */
1230 if (_PROP_TAG_MATCH(ctx, "dict") &&
1231 ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
1232 _PROP_FREE(tmpkey, M_TEMP);
1233 return (true);
1234 }
1235
1236 /* Ok, it must be a non-empty key start tag. */
1237 if (!_PROP_TAG_MATCH(ctx, "key") ||
1238 ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
1239 ctx->poic_is_empty_element)
1240 goto bad;
1241
1242 if (_prop_object_internalize_decode_string(ctx,
1243 tmpkey, PDK_MAXKEY, &keylen,
1244 &ctx->poic_cp) == false)
1245 goto bad;
1246
1247 _PROP_ASSERT(keylen <= PDK_MAXKEY);
1248 tmpkey[keylen] = '\0';
1249
1250 if (_prop_object_internalize_find_tag(ctx, "key",
1251 _PROP_TAG_TYPE_END) == false)
1252 goto bad;
1253
1254 /* ..and now the beginning of the value. */
1255 if (_prop_object_internalize_find_tag(ctx, NULL,
1256 _PROP_TAG_TYPE_START) == false)
1257 goto bad;
1258
1259 /*
1260 * Key is found, now wait for value to be parsed.
1261 */
1262 if (_prop_stack_push(stack, *obj,
1263 _prop_dictionary_internalize_continue,
1264 tmpkey, NULL))
1265 return (false);
1266
1267 bad:
1268 _PROP_FREE(tmpkey, M_TEMP);
1269 prop_object_release(dict);
1270 *obj = NULL;
1271 return (true);
1272 }
1273
1274 /*
1275 * prop_dictionary_internalize --
1276 * Create a dictionary by parsing the NUL-terminated XML-style
1277 * representation.
1278 */
1279 prop_dictionary_t
1280 prop_dictionary_internalize(const char *xml)
1281 {
1282 return _prop_generic_internalize(xml, "dict");
1283 }
1284
1285 #if !defined(_KERNEL) && !defined(_STANDALONE)
1286 /*
1287 * prop_dictionary_externalize_to_file --
1288 * Externalize a dictionary to the specified file.
1289 */
1290 bool
1291 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1292 {
1293 char *xml;
1294 bool rv;
1295 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */
1296
1297 xml = prop_dictionary_externalize(dict);
1298 if (xml == NULL)
1299 return (false);
1300 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
1301 if (rv == false)
1302 save_errno = errno;
1303 _PROP_FREE(xml, M_TEMP);
1304 if (rv == false)
1305 errno = save_errno;
1306
1307 return (rv);
1308 }
1309
1310 /*
1311 * prop_dictionary_internalize_from_file --
1312 * Internalize a dictionary from a file.
1313 */
1314 prop_dictionary_t
1315 prop_dictionary_internalize_from_file(const char *fname)
1316 {
1317 struct _prop_object_internalize_mapped_file *mf;
1318 prop_dictionary_t dict;
1319
1320 mf = _prop_object_internalize_map_file(fname);
1321 if (mf == NULL)
1322 return (NULL);
1323 dict = prop_dictionary_internalize(mf->poimf_xml);
1324 _prop_object_internalize_unmap_file(mf);
1325
1326 return (dict);
1327 }
1328 #endif /* !_KERNEL && !_STANDALONE */
1329