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