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