prop_dictionary.c revision 1.3 1 /* $NetBSD: prop_dictionary.c,v 1.3 2006/05/18 03:05:19 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 /*
44 * We implement these like arrays, but we keep them sorted by key.
45 * This allows us to binary-search as well as keep externalized output
46 * sane-looking for human eyes.
47 */
48
49 #define EXPAND_STEP 16
50
51 /*
52 * prop_dictionary_keysym_t is allocated with space at the end to hold the
53 * key. This must be a regular object so that we can maintain sane iterator
54 * semantics -- we don't want to require that the caller release the result
55 * of prop_object_iterator_next().
56 *
57 * We'd like to have some small'ish keysym objects for up-to-16 characters
58 * in a key, some for up-to-32 characters in a key, and then a final bucket
59 * for up-to-128 characters in a key (not including NUL). Keys longer than
60 * 128 characters are not allowed.
61 */
62 struct _prop_dictionary_keysym {
63 struct _prop_object pde_obj;
64 prop_object_t pde_objref;
65 size_t pde_size;
66 char pde_key[1];
67 /* actually variable length */
68 };
69
70 /* pde_key[1] takes care of the NUL */
71 #define PDE_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16)
72 #define PDE_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32)
73 #define PDE_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128)
74
75 #define PDE_MAXKEY 128
76
77 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDE_SIZE_16, "pdict16")
78 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDE_SIZE_32, "pdict32")
79 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDE_SIZE_128, "pdict128")
80
81 struct _prop_dictionary {
82 struct _prop_object pd_obj;
83 prop_dictionary_keysym_t *pd_array;
84 unsigned int pd_capacity;
85 unsigned int pd_count;
86 int pd_flags;
87
88 uint32_t pd_version;
89 };
90
91 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */
92
93 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
94 "propdict")
95 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
96 "property dictionary container object")
97
98 static void _prop_dictionary_free(void *);
99 static boolean_t _prop_dictionary_externalize(
100 struct _prop_object_externalize_context *,
101 void *);
102 static boolean_t _prop_dictionary_equals(void *, void *);
103
104 static const struct _prop_object_type _prop_object_type_dictionary = {
105 .pot_type = PROP_TYPE_DICTIONARY,
106 .pot_free = _prop_dictionary_free,
107 .pot_extern = _prop_dictionary_externalize,
108 .pot_equals = _prop_dictionary_equals,
109 };
110
111 static void _prop_dict_entry_free(void *);
112 static boolean_t _prop_dict_entry_externalize(
113 struct _prop_object_externalize_context *,
114 void *);
115 static boolean_t _prop_dict_entry_equals(void *, void *);
116
117 static const struct _prop_object_type _prop_object_type_dict_keysym = {
118 .pot_type = PROP_TYPE_DICT_KEYSYM,
119 .pot_free = _prop_dict_entry_free,
120 .pot_extern = _prop_dict_entry_externalize,
121 .pot_equals = _prop_dict_entry_equals,
122 };
123
124 #define prop_object_is_dictionary(x) \
125 ((x)->pd_obj.po_type == &_prop_object_type_dictionary)
126 #define prop_object_is_dictionary_keysym(x) \
127 ((x)->pde_obj.po_type == &_prop_object_type_dict_keysym)
128
129 #define prop_dictionary_is_immutable(x) \
130 (((x)->pd_flags & PD_F_IMMUTABLE) != 0)
131
132 struct _prop_dictionary_iterator {
133 struct _prop_object_iterator pdi_base;
134 unsigned int pdi_index;
135 };
136
137 static void
138 _prop_dict_entry_free(void *v)
139 {
140 prop_dictionary_keysym_t pde = v;
141 prop_object_t po;
142
143 _PROP_ASSERT(pde->pde_objref != NULL);
144 po = pde->pde_objref;
145
146 if (pde->pde_size <= PDE_SIZE_16)
147 _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pde);
148 else if (pde->pde_size <= PDE_SIZE_32)
149 _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pde);
150 else {
151 _PROP_ASSERT(pde->pde_size <= PDE_SIZE_128);
152 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pde);
153 }
154
155 prop_object_release(po);
156 }
157
158 static boolean_t
159 _prop_dict_entry_externalize(struct _prop_object_externalize_context *ctx,
160 void *v)
161 {
162 prop_dictionary_keysym_t pde = v;
163
164 /* We externalize these as strings, and they're never empty. */
165
166 _PROP_ASSERT(pde->pde_key[0] != '\0');
167
168 if (_prop_object_externalize_start_tag(ctx, "string") == FALSE ||
169 _prop_object_externalize_append_encoded_cstring(ctx,
170 pde->pde_key) == FALSE ||
171 _prop_object_externalize_end_tag(ctx, "string") == FALSE)
172 return (FALSE);
173
174 return (TRUE);
175 }
176
177 static boolean_t
178 _prop_dict_entry_equals(void *v1, void *v2)
179 {
180 prop_dictionary_keysym_t pde1 = v1;
181 prop_dictionary_keysym_t pde2 = v2;
182
183 _PROP_ASSERT(prop_object_is_dictionary_keysym(pde1));
184 _PROP_ASSERT(prop_object_is_dictionary_keysym(pde2));
185 if (pde1 == pde2)
186 return (TRUE);
187 return (strcmp(pde1->pde_key, pde2->pde_key) == 0);
188 }
189
190 static prop_dictionary_keysym_t
191 _prop_dict_entry_alloc(const char *key, prop_object_t obj)
192 {
193 prop_dictionary_keysym_t pde;
194 size_t size;
195
196 size = sizeof(*pde) + strlen(key) /* pde_key[1] covers the NUL */;
197
198 if (size <= PDE_SIZE_16)
199 pde = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
200 else if (size <= PDE_SIZE_32)
201 pde = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
202 else if (size <= PDE_SIZE_128)
203 pde = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
204 else
205 return (NULL); /* key too long */
206
207 if (pde != NULL) {
208 _prop_object_init(&pde->pde_obj,
209 &_prop_object_type_dict_keysym);
210
211 strcpy(pde->pde_key, key);
212
213 prop_object_retain(obj);
214 pde->pde_objref = obj;
215 pde->pde_size = size;
216 }
217 return (pde);
218 }
219
220 static void
221 _prop_dictionary_free(void *v)
222 {
223 prop_dictionary_t pd = v;
224 prop_dictionary_keysym_t pde;
225 unsigned int idx;
226
227 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
228 _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
229 (pd->pd_capacity != 0 && pd->pd_array != NULL));
230
231 for (idx = 0; idx < pd->pd_count; idx++) {
232 pde = pd->pd_array[idx];
233 _PROP_ASSERT(pde != NULL);
234 prop_object_release(pde);
235 }
236
237 if (pd->pd_array != NULL)
238 _PROP_FREE(pd->pd_array, M_PROP_DICT);
239
240 _PROP_POOL_PUT(_prop_dictionary_pool, pd);
241 }
242
243 static boolean_t
244 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
245 void *v)
246 {
247 prop_dictionary_t pd = v;
248 prop_dictionary_keysym_t pde;
249 struct _prop_object *po;
250 prop_object_iterator_t pi;
251 unsigned int i;
252
253 if (pd->pd_count == 0)
254 return (_prop_object_externalize_empty_tag(ctx, "dict"));
255
256 /* XXXJRT Hint "count" for the internalize step? */
257 if (_prop_object_externalize_start_tag(ctx, "dict") == FALSE ||
258 _prop_object_externalize_append_char(ctx, '\n') == FALSE)
259 return (FALSE);
260
261 pi = prop_dictionary_iterator(pd);
262 if (pi == NULL)
263 return (FALSE);
264
265 ctx->poec_depth++;
266 _PROP_ASSERT(ctx->poec_depth != 0);
267
268 while ((pde = prop_object_iterator_next(pi)) != NULL) {
269 po = prop_dictionary_get_keysym(pd, pde);
270 if (po == NULL ||
271 _prop_object_externalize_start_tag(ctx, "key") == FALSE ||
272 _prop_object_externalize_append_encoded_cstring(ctx,
273 pde->pde_key) == FALSE ||
274 _prop_object_externalize_end_tag(ctx, "key") == FALSE ||
275 (*po->po_type->pot_extern)(ctx, po) == FALSE) {
276 prop_object_iterator_release(pi);
277 return (FALSE);
278 }
279 }
280
281 prop_object_iterator_release(pi);
282
283 ctx->poec_depth--;
284 for (i = 0; i < ctx->poec_depth; i++) {
285 if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
286 return (FALSE);
287 }
288 if (_prop_object_externalize_end_tag(ctx, "dict") == FALSE)
289 return (FALSE);
290
291 return (TRUE);
292 }
293
294 static boolean_t
295 _prop_dictionary_equals(void *v1, void *v2)
296 {
297 prop_dictionary_t dict1 = v1;
298 prop_dictionary_t dict2 = v2;
299 prop_dictionary_keysym_t pde1, pde2;
300 unsigned int idx;
301
302 _PROP_ASSERT(prop_object_is_dictionary(dict1));
303 _PROP_ASSERT(prop_object_is_dictionary(dict2));
304 if (dict1 == dict2)
305 return (TRUE);
306 if (dict1->pd_count != dict2->pd_count)
307 return (FALSE);
308
309 for (idx = 0; idx < dict1->pd_count; idx++) {
310 pde1 = dict1->pd_array[idx];
311 pde2 = dict2->pd_array[idx];
312
313 if (prop_dictionary_keysym_equals(pde1, pde2) == FALSE)
314 return (FALSE);
315 if (prop_object_equals(pde1->pde_objref,
316 pde2->pde_objref) == FALSE)
317 return (FALSE);
318 }
319
320 return (TRUE);
321 }
322
323 static prop_dictionary_t
324 _prop_dictionary_alloc(unsigned int capacity)
325 {
326 prop_dictionary_t pd;
327 prop_dictionary_keysym_t *array;
328
329 if (capacity != 0) {
330 array = _PROP_CALLOC(capacity *
331 sizeof(prop_dictionary_keysym_t),
332 M_PROP_DICT);
333 if (array == NULL)
334 return (NULL);
335 } else
336 array = NULL;
337
338 pd = _PROP_POOL_GET(_prop_dictionary_pool);
339 if (pd != NULL) {
340 _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
341
342 pd->pd_array = array;
343 pd->pd_capacity = capacity;
344 pd->pd_count = 0;
345 pd->pd_flags = 0;
346
347 pd->pd_version = 0;
348 } else if (array != NULL)
349 _PROP_FREE(array, M_PROP_DICT);
350
351 return (pd);
352 }
353
354 static boolean_t
355 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
356 {
357 prop_dictionary_keysym_t *array, *oarray;
358
359 oarray = pd->pd_array;
360
361 array = _PROP_CALLOC(capacity * sizeof(prop_dictionary_keysym_t),
362 M_PROP_DICT);
363 if (array == NULL)
364 return (FALSE);
365 if (oarray != NULL)
366 memcpy(array, oarray,
367 pd->pd_capacity * sizeof(prop_dictionary_keysym_t));
368 pd->pd_array = array;
369 pd->pd_capacity = capacity;
370
371 if (oarray != NULL)
372 _PROP_FREE(oarray, M_PROP_DICT);
373
374 return (TRUE);
375 }
376
377 static prop_object_t
378 _prop_dictionary_iterator_next_object(void *v)
379 {
380 struct _prop_dictionary_iterator *pdi = v;
381 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
382 prop_dictionary_keysym_t pde;
383
384 _PROP_ASSERT(prop_object_is_dictionary(pd));
385
386 if (pd->pd_version != pdi->pdi_base.pi_version)
387 return (NULL); /* dictionary changed during iteration */
388
389 _PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
390
391 if (pdi->pdi_index == pd->pd_count)
392 return (NULL); /* we've iterated all objects */
393
394 pde = pd->pd_array[pdi->pdi_index];
395 pdi->pdi_index++;
396
397 return (pde);
398 }
399
400 static void
401 _prop_dictionary_iterator_reset(void *v)
402 {
403 struct _prop_dictionary_iterator *pdi = v;
404 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
405
406 _PROP_ASSERT(prop_object_is_dictionary(pd));
407
408 pdi->pdi_index = 0;
409 pdi->pdi_base.pi_version = pd->pd_version;
410 }
411
412 /*
413 * prop_dictionary_create --
414 * Create a dictionary.
415 */
416 prop_dictionary_t
417 prop_dictionary_create(void)
418 {
419
420 return (_prop_dictionary_alloc(0));
421 }
422
423 /*
424 * prop_dictionary_create_with_capacity --
425 * Create a dictionary with the capacity to store N objects.
426 */
427 prop_dictionary_t
428 prop_dictionary_create_with_capacity(unsigned int capacity)
429 {
430
431 return (_prop_dictionary_alloc(capacity));
432 }
433
434 /*
435 * prop_dictionary_copy --
436 * Copy a dictionary. The new dictionary contains refrences to
437 * the original dictionary's objects, not copies of those objects
438 * (i.e. a shallow copy).
439 */
440 prop_dictionary_t
441 prop_dictionary_copy(prop_dictionary_t opd)
442 {
443 prop_dictionary_t pd;
444 prop_dictionary_keysym_t pde;
445 unsigned int idx;
446
447 _PROP_ASSERT(prop_object_is_dictionary(opd));
448
449 if (prop_dictionary_is_immutable(opd) == FALSE)
450 return (prop_dictionary_copy_mutable(opd));
451
452 /*
453 * Copies of immutable dictionaries refrence the same
454 * dictionary entry objects to save space.
455 */
456
457 pd = _prop_dictionary_alloc(opd->pd_count);
458 if (pd != NULL) {
459 for (idx = 0; idx < opd->pd_count; idx++) {
460 pde = opd->pd_array[idx];
461 prop_object_retain(pde);
462 pd->pd_array[idx] = pde;
463 }
464 pd->pd_count = opd->pd_count;
465 pd->pd_flags = opd->pd_flags;
466 }
467 return (pd);
468 }
469
470 /*
471 * prop_dictionary_copy_mutable --
472 * Like prop_dictionary_copy(), but the resulting dictionary is
473 * mutable.
474 */
475 prop_dictionary_t
476 prop_dictionary_copy_mutable(prop_dictionary_t opd)
477 {
478 prop_dictionary_t pd;
479 prop_dictionary_keysym_t opde, pde;
480 unsigned int idx;
481
482 _PROP_ASSERT(prop_object_is_dictionary(opd));
483
484 pd = _prop_dictionary_alloc(opd->pd_count);
485 if (pd != NULL) {
486 for (idx = 0; idx > opd->pd_count; idx++) {
487 opde = opd->pd_array[idx];
488 pde = _prop_dict_entry_alloc(opde->pde_key,
489 opde->pde_objref);
490 if (pde == NULL) {
491 prop_object_release(pd);
492 return (NULL);
493 }
494 pd->pd_array[idx] = pde;
495 pd->pd_count++;
496 }
497 }
498 return (pd);
499 }
500
501 /*
502 * prop_dictionary_count --
503 * Return the number of objects stored in the dictionary.
504 */
505 unsigned int
506 prop_dictionary_count(prop_dictionary_t pd)
507 {
508
509 _PROP_ASSERT(prop_object_is_dictionary(pd));
510 return (pd->pd_count);
511 }
512
513 /*
514 * prop_dictionary_iterator --
515 * Return an iterator for the dictionary. The dictionary is retained by
516 * the iterator.
517 */
518 prop_object_iterator_t
519 prop_dictionary_iterator(prop_dictionary_t pd)
520 {
521 struct _prop_dictionary_iterator *pdi;
522
523 _PROP_ASSERT(prop_object_is_dictionary(pd));
524
525 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
526 if (pdi == NULL)
527 return (NULL);
528 pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
529 pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
530 prop_object_retain(pd);
531 pdi->pdi_base.pi_obj = pd;
532 pdi->pdi_base.pi_version = pd->pd_version;
533
534 _prop_dictionary_iterator_reset(pdi);
535
536 return (&pdi->pdi_base);
537 }
538
539 static prop_dictionary_keysym_t
540 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
541 unsigned int *idxp)
542 {
543 prop_dictionary_keysym_t pde;
544 unsigned int base, idx, distance;
545 int res;
546
547 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
548 distance >>= 1) {
549 idx = base + (distance >> 1);
550 pde = pd->pd_array[idx];
551 _PROP_ASSERT(pde != NULL);
552 res = strcmp(key, pde->pde_key);
553 if (res == 0) {
554 if (idxp != NULL)
555 *idxp = idx;
556 return (pde);
557 }
558 if (res > 0) { /* key > pde->pde_key: move right */
559 base = idx + 1;
560 distance--;
561 } /* else move left */
562 }
563
564 /* idx points to the slot we looked at last. */
565 if (idxp != NULL)
566 *idxp = idx;
567 return (NULL);
568 }
569
570 /*
571 * prop_dictionary_get --
572 * Return the object stored with specified key.
573 */
574 prop_object_t
575 prop_dictionary_get(prop_dictionary_t pd, const char *key)
576 {
577 prop_dictionary_keysym_t pde;
578
579 _PROP_ASSERT(prop_object_is_dictionary(pd));
580
581 pde = _prop_dict_lookup(pd, key, NULL);
582 if (pde != NULL) {
583 _PROP_ASSERT(pde->pde_objref != NULL);
584 return (pde->pde_objref);
585 }
586 return (NULL);
587 }
588
589 /*
590 * prop_dictionary_get_keysym --
591 * Return the object stored at the location encoded by the keysym.
592 */
593 prop_object_t
594 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pde)
595 {
596
597 _PROP_ASSERT(prop_object_is_dictionary(pd));
598 _PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
599 _PROP_ASSERT(pde->pde_objref != NULL);
600
601 return (pde->pde_objref);
602 }
603
604 /*
605 * prop_dictionary_set --
606 * Store a reference to an object at with the specified key.
607 * If the key already exisit, the original object is released.
608 */
609 boolean_t
610 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
611 {
612 prop_dictionary_keysym_t pde, opde;
613 unsigned int idx;
614
615 _PROP_ASSERT(prop_object_is_dictionary(pd));
616 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
617
618 if (prop_dictionary_is_immutable(pd))
619 return (FALSE);
620
621 pde = _prop_dict_lookup(pd, key, &idx);
622 if (pde != NULL) {
623 prop_object_t opo = pde->pde_objref;
624 prop_object_retain(po);
625 pde->pde_objref = po;
626 prop_object_release(opo);
627 return (TRUE);
628 }
629
630 pde = _prop_dict_entry_alloc(key, po);
631 if (pde == NULL)
632 return (FALSE);
633
634 if (pd->pd_count == pd->pd_capacity &&
635 _prop_dictionary_expand(pd, EXPAND_STEP) == FALSE) {
636 _prop_dict_entry_free(pde);
637 return (FALSE);
638 }
639
640 if (pd->pd_count == 0) {
641 pd->pd_array[0] = pde;
642 pd->pd_count++;
643 pd->pd_version++;
644 return (TRUE);
645 }
646
647 opde = pd->pd_array[idx];
648 _PROP_ASSERT(opde != NULL);
649
650 if (strcmp(key, opde->pde_key) < 0) {
651 /*
652 * key < opde->pde_key: insert to the left. This is
653 * the same as inserting to the right, except we decrement
654 * the current index first.
655 *
656 * Because we're unsigned, we have to special case 0
657 * (grumble).
658 */
659 if (idx == 0) {
660 memmove(&pd->pd_array[1], &pd->pd_array[0],
661 pd->pd_count * sizeof(*pde));
662 pd->pd_array[0] = pde;
663 pd->pd_count++;
664 pd->pd_version++;
665 return (TRUE);
666 }
667 idx--;
668 }
669
670 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
671 (pd->pd_count - (idx + 1)) * sizeof(*pde));
672 pd->pd_array[idx + 1] = pde;
673 pd->pd_count++;
674
675 pd->pd_version++;
676
677 return (TRUE);
678 }
679
680 /*
681 * prop_dictionary_set_keysym --
682 * Replace the object in the dictionary at the location encoded by
683 * the keysym.
684 */
685 boolean_t
686 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pde,
687 prop_object_t po)
688 {
689 prop_object_t opo;
690
691 _PROP_ASSERT(prop_object_is_dictionary(pd));
692 _PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
693 _PROP_ASSERT(pde->pde_objref != NULL);
694
695 if (prop_dictionary_is_immutable(pd))
696 return (FALSE);
697
698 opo = pde->pde_objref;
699 prop_object_retain(po);
700 pde->pde_objref = po;
701 prop_object_release(opo);
702 return (TRUE);
703 }
704
705 static void
706 _prop_dictionary_remove(prop_dictionary_t pd, prop_dictionary_keysym_t pde,
707 unsigned int idx)
708 {
709
710 _PROP_ASSERT(pd->pd_count != 0);
711 _PROP_ASSERT(idx < pd->pd_count);
712 _PROP_ASSERT(pd->pd_array[idx] == pde);
713
714 idx++;
715 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
716 (pd->pd_count - idx) * sizeof(*pde));
717 pd->pd_count--;
718 pd->pd_version++;
719
720 prop_object_release(pde);
721 }
722
723 /*
724 * prop_dictionary_remove --
725 * Remove the reference to an object with the specified key from
726 * the dictionary.
727 */
728 void
729 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
730 {
731 prop_dictionary_keysym_t pde;
732 unsigned int idx;
733
734 _PROP_ASSERT(prop_object_is_dictionary(pd));
735
736 /* XXX Should this be a _PROP_ASSERT()? */
737 if (prop_dictionary_is_immutable(pd))
738 return;
739
740 pde = _prop_dict_lookup(pd, key, &idx);
741 /* XXX Should this be a _PROP_ASSERT()? */
742 if (pde == NULL)
743 return;
744
745 _prop_dictionary_remove(pd, pde, idx);
746 }
747
748 /*
749 * prop_dictionary_remove_keysym --
750 * Remove a reference to an object stored in the dictionary at the
751 * location encoded by the keysym.
752 */
753 void
754 prop_dictionary_remove_keysym(prop_dictionary_t pd,
755 prop_dictionary_keysym_t pde)
756 {
757 prop_dictionary_keysym_t opde;
758 unsigned int idx;
759
760 _PROP_ASSERT(prop_object_is_dictionary(pd));
761 _PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
762 _PROP_ASSERT(pde->pde_objref != NULL);
763
764 /* XXX Should this be a _PROP_ASSERT()? */
765 if (prop_dictionary_is_immutable(pd))
766 return;
767
768 opde = _prop_dict_lookup(pd, pde->pde_key, &idx);
769 _PROP_ASSERT(opde == pde);
770
771 _prop_dictionary_remove(pd, opde, idx);
772 }
773
774 /*
775 * prop_dictionary_equals --
776 * Return TRUE if the two dictionaries are equivalent. Note we do a
777 * by-value comparison of the objects in the dictionary.
778 */
779 boolean_t
780 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
781 {
782
783 return (_prop_dictionary_equals(dict1, dict2));
784 }
785
786 /*
787 * prop_dictionary_keysym_cstring_nocopy --
788 * Return an immutable reference to the keysym's value.
789 */
790 const char *
791 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pde)
792 {
793
794 _PROP_ASSERT(prop_object_is_dictionary_keysym(pde));
795 return (pde->pde_key);
796 }
797
798 /*
799 * prop_dictionary_keysym_equals --
800 * Return TRUE if the two dictionary key symbols are equivalent.
801 * Note: We do not compare the object references.
802 */
803 boolean_t
804 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pde1,
805 prop_dictionary_keysym_t pde2)
806 {
807
808 return (_prop_dict_entry_equals(pde1, pde2));
809 }
810
811 /*
812 * prop_dictionary_externalize --
813 * Externalize a dictionary, returning a NUL-terminated buffer
814 * containing the XML-style representation. The buffer is allocated
815 * with the M_TEMP memory type.
816 */
817 char *
818 prop_dictionary_externalize(prop_dictionary_t pd)
819 {
820 struct _prop_object_externalize_context *ctx;
821 char *cp;
822
823 ctx = _prop_object_externalize_context_alloc();
824 if (ctx == NULL)
825 return (NULL);
826
827 if (_prop_object_externalize_start_tag(ctx,
828 "plist version=\"1.0\"") == FALSE ||
829 _prop_object_externalize_append_char(ctx, '\n') == FALSE ||
830 (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == FALSE ||
831 _prop_object_externalize_end_tag(ctx, "plist") == FALSE ||
832 _prop_object_externalize_append_char(ctx, '\0') == FALSE) {
833 /* We are responsible for releasing the buffer. */
834 _PROP_FREE(ctx->poec_buf, M_TEMP);
835 _prop_object_externalize_context_free(ctx);
836 return (NULL);
837 }
838
839 cp = ctx->poec_buf;
840 _prop_object_externalize_context_free(ctx);
841
842 return (cp);
843 }
844
845 /*
846 * _prop_dictionary_internalize --
847 * Parse a <dict>...</dict> and return the object created from the
848 * external representation.
849 */
850 prop_object_t
851 _prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
852 {
853 prop_dictionary_t dict;
854 prop_object_t val;
855 size_t keylen;
856 char *tmpkey;
857
858 /* We don't currently understand any attributes. */
859 if (ctx->poic_tagattr != NULL)
860 return (NULL);
861
862 dict = prop_dictionary_create();
863 if (dict == NULL)
864 return (NULL);
865
866 if (ctx->poic_is_empty_element)
867 return (dict);
868
869 tmpkey = _PROP_MALLOC(PDE_MAXKEY + 1, M_TEMP);
870 if (tmpkey == NULL)
871 goto bad;
872
873 for (;;) {
874 /* Fetch the next tag. */
875 if (_prop_object_internalize_find_tag(ctx, NULL,
876 _PROP_TAG_TYPE_EITHER) == FALSE)
877 goto bad;
878
879 /* Check to see if this is the end of the dictionary. */
880 if (_PROP_TAG_MATCH(ctx, "dict") &&
881 ctx->poic_tag_type == _PROP_TAG_TYPE_END)
882 break;
883
884 /* Ok, it must be a non-empty key start tag. */
885 if (!_PROP_TAG_MATCH(ctx, "key") ||
886 ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
887 ctx->poic_is_empty_element)
888 goto bad;
889
890 if (_prop_object_internalize_decode_string(ctx,
891 tmpkey, PDE_MAXKEY, &keylen,
892 &ctx->poic_cp) == FALSE)
893 goto bad;
894
895 _PROP_ASSERT(keylen <= PDE_MAXKEY);
896 tmpkey[keylen] = '\0';
897
898 if (_prop_object_internalize_find_tag(ctx, "key",
899 _PROP_TAG_TYPE_END) == FALSE)
900 goto bad;
901
902 /* ..and now the beginning of the value. */
903 if (_prop_object_internalize_find_tag(ctx, NULL,
904 _PROP_TAG_TYPE_START) == FALSE)
905 goto bad;
906
907 val = _prop_object_internalize_by_tag(ctx);
908 if (val == NULL)
909 goto bad;
910
911 if (prop_dictionary_set(dict, tmpkey, val) == FALSE) {
912 prop_object_release(val);
913 goto bad;
914 }
915 prop_object_release(val);
916 }
917
918 _PROP_FREE(tmpkey, M_TEMP);
919 return (dict);
920
921 bad:
922 if (tmpkey != NULL)
923 _PROP_FREE(tmpkey, M_TEMP);
924 prop_object_release(dict);
925 return (NULL);
926 }
927
928 /*
929 * prop_dictionary_internalize --
930 * Create a dictionary by parsing the NUL-terminated XML-style
931 * representation.
932 */
933 prop_dictionary_t
934 prop_dictionary_internalize(const char *xml)
935 {
936 prop_dictionary_t dict = NULL;
937 struct _prop_object_internalize_context *ctx;
938
939 ctx = _prop_object_internalize_context_alloc(xml);
940 if (ctx == NULL)
941 return (NULL);
942
943 /* We start with a <plist> tag. */
944 if (_prop_object_internalize_find_tag(ctx, "plist",
945 _PROP_TAG_TYPE_START) == FALSE)
946 goto out;
947
948 /* Plist elements cannot be empty. */
949 if (ctx->poic_is_empty_element)
950 goto out;
951
952 /*
953 * We don't understand any plist attributes, but Apple XML
954 * property lists often have a "version" attibute. If we
955 * see that one, we simply ignore it.
956 */
957 if (ctx->poic_tagattr != NULL &&
958 !_PROP_TAGATTR_MATCH(ctx, "version"))
959 goto out;
960
961 /* Next we expect to see <dict>. */
962 if (_prop_object_internalize_find_tag(ctx, "dict",
963 _PROP_TAG_TYPE_START) == FALSE)
964 goto out;
965
966 dict = _prop_dictionary_internalize(ctx);
967 if (dict == NULL)
968 goto out;
969
970 /* We've advanced past </dict>. Now we want </plist>. */
971 if (_prop_object_internalize_find_tag(ctx, "plist",
972 _PROP_TAG_TYPE_END) == FALSE) {
973 prop_object_release(dict);
974 dict = NULL;
975 }
976
977 out:
978 _prop_object_internalize_context_free(ctx);
979 return (dict);
980 }
981