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