prop_dictionary.c revision 1.51 1 /* $NetBSD: prop_dictionary.c,v 1.51 2025/05/14 03:25:46 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2007, 2020, 2025 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_object_impl.h"
33 #include <prop/prop_array.h>
34 #include <prop/prop_dictionary.h>
35 #include <prop/prop_string.h>
36
37 #include <sys/rbtree.h>
38
39 #if !defined(_KERNEL) && !defined(_STANDALONE)
40 #include <errno.h>
41 #endif
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 pdk_obj;
64 size_t pdk_size;
65 struct rb_node pdk_link;
66 char pdk_key[1];
67 /* actually variable length */
68 };
69
70 /* pdk_key[1] takes care of the NUL */
71 #define PDK_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16)
72 #define PDK_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32)
73 #define PDK_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128)
74
75 #define PDK_MAXKEY 128
76
77 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16")
78 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32")
79 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128")
80
81 struct _prop_dict_entry {
82 prop_dictionary_keysym_t pde_key;
83 prop_object_t pde_objref;
84 };
85
86 struct _prop_dictionary {
87 struct _prop_object pd_obj;
88 _PROP_RWLOCK_DECL(pd_rwlock)
89 struct _prop_dict_entry *pd_array;
90 unsigned int pd_capacity;
91 unsigned int pd_count;
92 int pd_flags;
93
94 uint32_t pd_version;
95 };
96
97 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */
98
99 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
100 "propdict")
101 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
102 "property dictionary container object")
103
104 static const struct _prop_object_type_tags _prop_dictionary_type_tags = {
105 .xml_tag = "dict",
106 .json_open_tag = "{",
107 .json_close_tag = "}",
108 .json_empty_sep = " ",
109 };
110
111 static const struct _prop_object_type_tags _prop_dict_key_type_tags = {
112 .xml_tag = "key",
113 .json_open_tag = "\"",
114 .json_close_tag = "\"",
115 };
116
117 struct _prop_dictionary_iterator {
118 struct _prop_object_iterator pdi_base;
119 unsigned int pdi_index;
120 };
121
122 static _prop_object_free_rv_t
123 _prop_dictionary_free(prop_stack_t, prop_object_t *);
124 static void _prop_dictionary_emergency_free(prop_object_t);
125 static bool _prop_dictionary_externalize(
126 struct _prop_object_externalize_context *,
127 void *);
128 static _prop_object_equals_rv_t
129 _prop_dictionary_equals(prop_object_t, prop_object_t,
130 void **, void **,
131 prop_object_t *, prop_object_t *);
132 static void _prop_dictionary_equals_finish(prop_object_t, prop_object_t);
133 static struct _prop_dictionary_iterator *
134 _prop_dictionary_iterator_locked(prop_dictionary_t);
135 static prop_object_t
136 _prop_dictionary_iterator_next_object_locked(void *);
137 static prop_object_t
138 _prop_dictionary_get_keysym(prop_dictionary_t,
139 prop_dictionary_keysym_t, bool);
140 static prop_object_t
141 _prop_dictionary_get(prop_dictionary_t, const char *, bool);
142
143 static void _prop_dictionary_lock(void);
144 static void _prop_dictionary_unlock(void);
145
146 static const struct _prop_object_type _prop_object_type_dictionary = {
147 .pot_type = PROP_TYPE_DICTIONARY,
148 .pot_free = _prop_dictionary_free,
149 .pot_emergency_free = _prop_dictionary_emergency_free,
150 .pot_extern = _prop_dictionary_externalize,
151 .pot_equals = _prop_dictionary_equals,
152 .pot_equals_finish = _prop_dictionary_equals_finish,
153 .pot_lock = _prop_dictionary_lock,
154 .pot_unlock = _prop_dictionary_unlock,
155 };
156
157 static _prop_object_free_rv_t
158 _prop_dict_keysym_free(prop_stack_t, prop_object_t *);
159 static bool _prop_dict_keysym_externalize(
160 struct _prop_object_externalize_context *,
161 void *);
162 static _prop_object_equals_rv_t
163 _prop_dict_keysym_equals(prop_object_t, prop_object_t,
164 void **, void **,
165 prop_object_t *, prop_object_t *);
166
167 static const struct _prop_object_type _prop_object_type_dict_keysym = {
168 .pot_type = PROP_TYPE_DICT_KEYSYM,
169 .pot_free = _prop_dict_keysym_free,
170 .pot_extern = _prop_dict_keysym_externalize,
171 .pot_equals = _prop_dict_keysym_equals,
172 };
173
174 #define prop_object_is_dictionary(x) \
175 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary)
176 #define prop_object_is_dictionary_keysym(x) \
177 ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym)
178
179 #define prop_dictionary_is_immutable(x) \
180 (((x)->pd_flags & PD_F_IMMUTABLE) != 0)
181
182 /*
183 * Dictionary key symbols are immutable, and we are likely to have many
184 * duplicated key symbols. So, to save memory, we unique'ify key symbols
185 * so we only have to have one copy of each string.
186 */
187
188 static int
189 /*ARGSUSED*/
190 _prop_dict_keysym_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED,
191 const void *n1, const void *n2)
192 {
193 const struct _prop_dictionary_keysym *pdk1 = n1;
194 const struct _prop_dictionary_keysym *pdk2 = n2;
195
196 return strcmp(pdk1->pdk_key, pdk2->pdk_key);
197 }
198
199 static int
200 /*ARGSUSED*/
201 _prop_dict_keysym_rb_compare_key(void *ctx _PROP_ARG_UNUSED,
202 const void *n, const void *v)
203 {
204 const struct _prop_dictionary_keysym *pdk = n;
205 const char *cp = v;
206
207 return strcmp(pdk->pdk_key, cp);
208 }
209
210 static const rb_tree_ops_t _prop_dict_keysym_rb_tree_ops = {
211 .rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes,
212 .rbto_compare_key = _prop_dict_keysym_rb_compare_key,
213 .rbto_node_offset = offsetof(struct _prop_dictionary_keysym, pdk_link),
214 .rbto_context = NULL
215 };
216
217 static struct rb_tree _prop_dict_keysym_tree;
218
219 _PROP_ONCE_DECL(_prop_dict_init_once)
220 _PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex)
221
222 static int
223 _prop_dict_init(void)
224 {
225
226 _PROP_MUTEX_INIT(_prop_dict_keysym_tree_mutex);
227 rb_tree_init(&_prop_dict_keysym_tree,
228 &_prop_dict_keysym_rb_tree_ops);
229 return 0;
230 }
231
232 static void
233 _prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
234 {
235
236 if (pdk->pdk_size <= PDK_SIZE_16)
237 _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk);
238 else if (pdk->pdk_size <= PDK_SIZE_32)
239 _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk);
240 else {
241 _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128);
242 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk);
243 }
244 }
245
246 /* ARGSUSED */
247 static _prop_object_free_rv_t
248 _prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj)
249 {
250 prop_dictionary_keysym_t pdk = *obj;
251
252 rb_tree_remove_node(&_prop_dict_keysym_tree, pdk);
253 _prop_dict_keysym_put(pdk);
254
255 return _PROP_OBJECT_FREE_DONE;
256 }
257
258 static bool
259 _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
260 void *v)
261 {
262 prop_dictionary_keysym_t pdk = v;
263
264 /* We externalize these as strings, and they're never empty. */
265
266 _PROP_ASSERT(pdk->pdk_key[0] != '\0');
267
268 return _prop_string_externalize_internal(ctx, &_prop_dict_key_type_tags,
269 pdk->pdk_key);
270 }
271
272 /* ARGSUSED */
273 static _prop_object_equals_rv_t
274 _prop_dict_keysym_equals(prop_object_t v1, prop_object_t v2,
275 void **stored_pointer1, void **stored_pointer2,
276 prop_object_t *next_obj1, prop_object_t *next_obj2)
277 {
278 prop_dictionary_keysym_t pdk1 = v1;
279 prop_dictionary_keysym_t pdk2 = v2;
280
281 /*
282 * There is only ever one copy of a keysym at any given time,
283 * so we can reduce this to a simple pointer equality check.
284 */
285 if (pdk1 == pdk2)
286 return _PROP_OBJECT_EQUALS_TRUE;
287 else
288 return _PROP_OBJECT_EQUALS_FALSE;
289 }
290
291 static prop_dictionary_keysym_t
292 _prop_dict_keysym_alloc(const char *key)
293 {
294 prop_dictionary_keysym_t opdk, pdk, rpdk;
295 size_t size;
296
297 _PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init);
298
299 /*
300 * Check to see if this already exists in the tree. If it does,
301 * we just retain it and return it.
302 */
303 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
304 opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key);
305 if (opdk != NULL) {
306 prop_object_retain(opdk);
307 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
308 return (opdk);
309 }
310 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
311
312 /*
313 * Not in the tree. Create it now.
314 */
315
316 size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */;
317
318 if (size <= PDK_SIZE_16)
319 pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
320 else if (size <= PDK_SIZE_32)
321 pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
322 else if (size <= PDK_SIZE_128)
323 pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
324 else
325 pdk = NULL; /* key too long */
326
327 if (pdk == NULL)
328 return (NULL);
329
330 _prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym);
331
332 strcpy(pdk->pdk_key, key);
333 pdk->pdk_size = size;
334
335 /*
336 * We dropped the mutex when we allocated the new object, so
337 * we have to check again if it is in the tree.
338 */
339 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
340 opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key);
341 if (opdk != NULL) {
342 prop_object_retain(opdk);
343 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
344 _prop_dict_keysym_put(pdk);
345 return (opdk);
346 }
347 rpdk = rb_tree_insert_node(&_prop_dict_keysym_tree, pdk);
348 _PROP_ASSERT(rpdk == pdk);
349 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
350 return (rpdk);
351 }
352
353 static _prop_object_free_rv_t
354 _prop_dictionary_free(prop_stack_t stack, prop_object_t *obj)
355 {
356 prop_dictionary_t pd = *obj;
357 prop_dictionary_keysym_t pdk;
358 prop_object_t po;
359
360 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
361 _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
362 (pd->pd_capacity != 0 && pd->pd_array != NULL));
363
364 /* The empty dictorinary is easy, handle that first. */
365 if (pd->pd_count == 0) {
366 if (pd->pd_array != NULL)
367 _PROP_FREE(pd->pd_array, M_PROP_DICT);
368
369 _PROP_RWLOCK_DESTROY(pd->pd_rwlock);
370
371 _PROP_POOL_PUT(_prop_dictionary_pool, pd);
372
373 return (_PROP_OBJECT_FREE_DONE);
374 }
375
376 po = pd->pd_array[pd->pd_count - 1].pde_objref;
377 _PROP_ASSERT(po != NULL);
378
379 if (stack == NULL) {
380 /*
381 * If we are in emergency release mode,
382 * just let caller recurse down.
383 */
384 *obj = po;
385 return (_PROP_OBJECT_FREE_FAILED);
386 }
387
388 /* Otherwise, try to push the current object on the stack. */
389 if (!_prop_stack_push(stack, pd, NULL, NULL, NULL)) {
390 /* Push failed, entering emergency release mode. */
391 return (_PROP_OBJECT_FREE_FAILED);
392 }
393 /* Object pushed on stack, caller will release it. */
394 --pd->pd_count;
395 pdk = pd->pd_array[pd->pd_count].pde_key;
396 _PROP_ASSERT(pdk != NULL);
397
398 prop_object_release(pdk);
399
400 *obj = po;
401 return (_PROP_OBJECT_FREE_RECURSE);
402 }
403
404
405 static void
406 _prop_dictionary_lock(void)
407 {
408
409 /* XXX: once necessary or paranoia? */
410 _PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init);
411 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
412 }
413
414 static void
415 _prop_dictionary_unlock(void)
416 {
417 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
418 }
419
420 static void
421 _prop_dictionary_emergency_free(prop_object_t obj)
422 {
423 prop_dictionary_t pd = obj;
424 prop_dictionary_keysym_t pdk;
425
426 _PROP_ASSERT(pd->pd_count != 0);
427 --pd->pd_count;
428
429 pdk = pd->pd_array[pd->pd_count].pde_key;
430 _PROP_ASSERT(pdk != NULL);
431 prop_object_release(pdk);
432 }
433
434 static bool
435 _prop_dictionary_externalize_one(struct _prop_object_externalize_context *ctx,
436 prop_dictionary_keysym_t pdk, struct _prop_object *po)
437 {
438 if (po == NULL) {
439 return false;
440 }
441
442 if (_prop_string_externalize_internal(ctx,
443 &_prop_dict_key_type_tags,
444 pdk->pdk_key) == false) {
445 return false;
446 }
447
448 switch (ctx->poec_format) {
449 case PROP_FORMAT_JSON:
450 if (_prop_extern_append_cstring(ctx, ": ") == false) {
451 return false;
452 }
453 break;
454
455 default: /* XML */
456 if (_prop_extern_end_line(ctx, NULL) == false ||
457 _prop_extern_start_line(ctx) == false) {
458 return false;
459 }
460 break;
461 }
462
463 return (*po->po_type->pot_extern)(ctx, po);
464 }
465
466 static bool
467 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
468 void *v)
469 {
470 prop_dictionary_t pd = v;
471 prop_dictionary_keysym_t pdk;
472 struct _prop_object *po;
473 struct _prop_dictionary_iterator *pdi;
474 bool rv = false;
475 const char * const sep =
476 ctx->poec_format == PROP_FORMAT_JSON ? "," : NULL;
477
478 _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML ||
479 ctx->poec_format == PROP_FORMAT_JSON);
480
481 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
482
483 if (pd->pd_count == 0) {
484 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
485 return (_prop_extern_append_empty_tag(ctx,
486 &_prop_dictionary_type_tags));
487 }
488
489 if (_prop_extern_append_start_tag(ctx,
490 &_prop_dictionary_type_tags, NULL) == false ||
491 _prop_extern_end_line(ctx, NULL) == false)
492 goto out;
493
494 pdi = _prop_dictionary_iterator_locked(pd);
495 if (pdi == NULL)
496 goto out;
497
498 ctx->poec_depth++;
499 _PROP_ASSERT(ctx->poec_depth != 0);
500
501 while ((pdk = _prop_dictionary_iterator_next_object_locked(pdi))
502 != NULL) {
503 po = _prop_dictionary_get_keysym(pd, pdk, true);
504 if (_prop_extern_start_line(ctx) == false ||
505 _prop_dictionary_externalize_one(ctx, pdk, po) == false ||
506 _prop_extern_end_line(ctx,
507 pdi->pdi_index < pd->pd_count ?
508 sep : NULL) == false) {
509 prop_object_iterator_release(&pdi->pdi_base);
510 goto out;
511 }
512 }
513
514 prop_object_iterator_release(&pdi->pdi_base);
515
516 ctx->poec_depth--;
517 if (_prop_extern_start_line(ctx) == false ||
518 _prop_extern_append_end_tag(ctx,
519 &_prop_dictionary_type_tags) == false) {
520
521 goto out;
522 }
523
524 rv = true;
525
526 out:
527 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
528 return (rv);
529 }
530
531 /* ARGSUSED */
532 static _prop_object_equals_rv_t
533 _prop_dictionary_equals(prop_object_t v1, prop_object_t v2,
534 void **stored_pointer1, void **stored_pointer2,
535 prop_object_t *next_obj1, prop_object_t *next_obj2)
536 {
537 prop_dictionary_t dict1 = v1;
538 prop_dictionary_t dict2 = v2;
539 uintptr_t idx;
540 _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE;
541
542 if (dict1 == dict2)
543 return (_PROP_OBJECT_EQUALS_TRUE);
544
545 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
546
547 idx = (uintptr_t)*stored_pointer1;
548
549 if (idx == 0) {
550 if ((uintptr_t)dict1 < (uintptr_t)dict2) {
551 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
552 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
553 } else {
554 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
555 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
556 }
557 }
558
559 if (dict1->pd_count != dict2->pd_count)
560 goto out;
561
562 if (idx == dict1->pd_count) {
563 rv = _PROP_OBJECT_EQUALS_TRUE;
564 goto out;
565 }
566
567 _PROP_ASSERT(idx < dict1->pd_count);
568
569 *stored_pointer1 = (void *)(idx + 1);
570 *stored_pointer2 = (void *)(idx + 1);
571
572 *next_obj1 = dict1->pd_array[idx].pde_objref;
573 *next_obj2 = dict2->pd_array[idx].pde_objref;
574
575 if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key,
576 dict2->pd_array[idx].pde_key))
577 goto out;
578
579 return (_PROP_OBJECT_EQUALS_RECURSE);
580
581 out:
582 _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
583 _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
584 return (rv);
585 }
586
587 static void
588 _prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2)
589 {
590 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock);
591 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock);
592 }
593
594 static prop_dictionary_t
595 _prop_dictionary_alloc(unsigned int capacity)
596 {
597 prop_dictionary_t pd;
598 struct _prop_dict_entry *array;
599
600 if (capacity != 0) {
601 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
602 if (array == NULL)
603 return (NULL);
604 } else
605 array = NULL;
606
607 pd = _PROP_POOL_GET(_prop_dictionary_pool);
608 if (pd != NULL) {
609 _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
610
611 _PROP_RWLOCK_INIT(pd->pd_rwlock);
612 pd->pd_array = array;
613 pd->pd_capacity = capacity;
614 pd->pd_count = 0;
615 pd->pd_flags = 0;
616
617 pd->pd_version = 0;
618 } else if (array != NULL)
619 _PROP_FREE(array, M_PROP_DICT);
620
621 return (pd);
622 }
623
624 static bool
625 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
626 {
627 struct _prop_dict_entry *array, *oarray;
628
629 /*
630 * Dictionary must be WRITE-LOCKED.
631 */
632
633 oarray = pd->pd_array;
634
635 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
636 if (array == NULL)
637 return (false);
638 if (oarray != NULL)
639 memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
640 pd->pd_array = array;
641 pd->pd_capacity = capacity;
642
643 if (oarray != NULL)
644 _PROP_FREE(oarray, M_PROP_DICT);
645
646 return (true);
647 }
648
649 static prop_object_t
650 _prop_dictionary_iterator_next_object_locked(void *v)
651 {
652 struct _prop_dictionary_iterator *pdi = v;
653 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
654 prop_dictionary_keysym_t pdk = NULL;
655
656 _PROP_ASSERT(prop_object_is_dictionary(pd));
657
658 if (pd->pd_version != pdi->pdi_base.pi_version)
659 goto out; /* dictionary changed during iteration */
660
661 _PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
662
663 if (pdi->pdi_index == pd->pd_count)
664 goto out; /* we've iterated all objects */
665
666 pdk = pd->pd_array[pdi->pdi_index].pde_key;
667 pdi->pdi_index++;
668
669 out:
670 return (pdk);
671 }
672
673 static prop_object_t
674 _prop_dictionary_iterator_next_object(void *v)
675 {
676 struct _prop_dictionary_iterator *pdi = v;
677 prop_dictionary_t pd _PROP_ARG_UNUSED = pdi->pdi_base.pi_obj;
678 prop_dictionary_keysym_t pdk;
679
680 _PROP_ASSERT(prop_object_is_dictionary(pd));
681
682 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
683 pdk = _prop_dictionary_iterator_next_object_locked(pdi);
684 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
685 return (pdk);
686 }
687
688 static void
689 _prop_dictionary_iterator_reset_locked(void *v)
690 {
691 struct _prop_dictionary_iterator *pdi = v;
692 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
693
694 _PROP_ASSERT(prop_object_is_dictionary(pd));
695
696 pdi->pdi_index = 0;
697 pdi->pdi_base.pi_version = pd->pd_version;
698 }
699
700 static void
701 _prop_dictionary_iterator_reset(void *v)
702 {
703 struct _prop_dictionary_iterator *pdi = v;
704 prop_dictionary_t pd _PROP_ARG_UNUSED = pdi->pdi_base.pi_obj;
705
706 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
707 _prop_dictionary_iterator_reset_locked(pdi);
708 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
709 }
710
711 /*
712 * prop_dictionary_create --
713 * Create a dictionary.
714 */
715 _PROP_EXPORT prop_dictionary_t
716 prop_dictionary_create(void)
717 {
718
719 return (_prop_dictionary_alloc(0));
720 }
721
722 /*
723 * prop_dictionary_create_with_capacity --
724 * Create a dictionary with the capacity to store N objects.
725 */
726 _PROP_EXPORT prop_dictionary_t
727 prop_dictionary_create_with_capacity(unsigned int capacity)
728 {
729
730 return (_prop_dictionary_alloc(capacity));
731 }
732
733 /*
734 * prop_dictionary_copy --
735 * Copy a dictionary. The new dictionary has an initial capacity equal
736 * to the number of objects stored int the original dictionary. The new
737 * dictionary contains references to the original dictionary's objects,
738 * not copies of those objects (i.e. a shallow copy).
739 */
740 _PROP_EXPORT prop_dictionary_t
741 prop_dictionary_copy(prop_dictionary_t opd)
742 {
743 prop_dictionary_t pd;
744 prop_dictionary_keysym_t pdk;
745 prop_object_t po;
746 unsigned int idx;
747
748 if (! prop_object_is_dictionary(opd))
749 return (NULL);
750
751 _PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
752
753 pd = _prop_dictionary_alloc(opd->pd_count);
754 if (pd != NULL) {
755 for (idx = 0; idx < opd->pd_count; idx++) {
756 pdk = opd->pd_array[idx].pde_key;
757 po = opd->pd_array[idx].pde_objref;
758
759 prop_object_retain(pdk);
760 prop_object_retain(po);
761
762 pd->pd_array[idx].pde_key = pdk;
763 pd->pd_array[idx].pde_objref = po;
764 }
765 pd->pd_count = opd->pd_count;
766 pd->pd_flags = opd->pd_flags;
767 }
768 _PROP_RWLOCK_UNLOCK(opd->pd_rwlock);
769 return (pd);
770 }
771
772 /*
773 * prop_dictionary_copy_mutable --
774 * Like prop_dictionary_copy(), but the resulting dictionary is
775 * mutable.
776 */
777 _PROP_EXPORT prop_dictionary_t
778 prop_dictionary_copy_mutable(prop_dictionary_t opd)
779 {
780 prop_dictionary_t pd;
781
782 if (! prop_object_is_dictionary(opd))
783 return (NULL);
784
785 pd = prop_dictionary_copy(opd);
786 if (pd != NULL)
787 pd->pd_flags &= ~PD_F_IMMUTABLE;
788
789 return (pd);
790 }
791
792 /*
793 * prop_dictionary_make_immutable --
794 * Set the immutable flag on that dictionary.
795 */
796 _PROP_EXPORT void
797 prop_dictionary_make_immutable(prop_dictionary_t pd)
798 {
799
800 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
801 if (prop_dictionary_is_immutable(pd) == false)
802 pd->pd_flags |= PD_F_IMMUTABLE;
803 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
804 }
805
806 /*
807 * prop_dictionary_count --
808 * Return the number of objects stored in the dictionary.
809 */
810 _PROP_EXPORT unsigned int
811 prop_dictionary_count(prop_dictionary_t pd)
812 {
813 unsigned int rv;
814
815 if (! prop_object_is_dictionary(pd))
816 return (0);
817
818 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
819 rv = pd->pd_count;
820 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
821
822 return (rv);
823 }
824
825 /*
826 * prop_dictionary_ensure_capacity --
827 * Ensure that the dictionary has the capacity to store the specified
828 * total number of objects (including the objects already stored in
829 * the dictionary).
830 */
831 _PROP_EXPORT bool
832 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
833 {
834 bool rv;
835
836 if (! prop_object_is_dictionary(pd))
837 return (false);
838
839 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
840 if (capacity > pd->pd_capacity)
841 rv = _prop_dictionary_expand(pd, capacity);
842 else
843 rv = true;
844 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
845 return (rv);
846 }
847
848 static struct _prop_dictionary_iterator *
849 _prop_dictionary_iterator_locked(prop_dictionary_t pd)
850 {
851 struct _prop_dictionary_iterator *pdi;
852
853 if (! prop_object_is_dictionary(pd))
854 return (NULL);
855
856 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
857 if (pdi == NULL)
858 return (NULL);
859 pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
860 pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
861 prop_object_retain(pd);
862 pdi->pdi_base.pi_obj = pd;
863 _prop_dictionary_iterator_reset_locked(pdi);
864
865 return pdi;
866 }
867
868 /*
869 * prop_dictionary_iterator --
870 * Return an iterator for the dictionary. The dictionary is retained by
871 * the iterator.
872 */
873 _PROP_EXPORT prop_object_iterator_t
874 prop_dictionary_iterator(prop_dictionary_t pd)
875 {
876 struct _prop_dictionary_iterator *pdi;
877
878 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
879 pdi = _prop_dictionary_iterator_locked(pd);
880 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
881 return &pdi->pdi_base;
882 }
883
884 /*
885 * prop_dictionary_all_keys --
886 * Return an array containing a snapshot of all of the keys
887 * in the dictionary.
888 */
889 _PROP_EXPORT prop_array_t
890 prop_dictionary_all_keys(prop_dictionary_t pd)
891 {
892 prop_array_t array;
893 unsigned int idx;
894 bool rv = true;
895
896 if (! prop_object_is_dictionary(pd))
897 return (NULL);
898
899 /* There is no pressing need to lock the dictionary for this. */
900 array = prop_array_create_with_capacity(pd->pd_count);
901
902 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
903
904 for (idx = 0; idx < pd->pd_count; idx++) {
905 rv = prop_array_add(array, pd->pd_array[idx].pde_key);
906 if (rv == false)
907 break;
908 }
909
910 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
911
912 if (rv == false) {
913 prop_object_release(array);
914 array = NULL;
915 }
916 return (array);
917 }
918
919 static struct _prop_dict_entry *
920 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
921 unsigned int *idxp)
922 {
923 struct _prop_dict_entry *pde;
924 unsigned int base, idx, distance;
925 int res;
926
927 /*
928 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
929 */
930
931 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
932 distance >>= 1) {
933 idx = base + (distance >> 1);
934 pde = &pd->pd_array[idx];
935 _PROP_ASSERT(pde->pde_key != NULL);
936 res = strcmp(key, pde->pde_key->pdk_key);
937 if (res == 0) {
938 if (idxp != NULL)
939 *idxp = idx;
940 return (pde);
941 }
942 if (res > 0) { /* key > pdk_key: move right */
943 base = idx + 1;
944 distance--;
945 } /* else move left */
946 }
947
948 /* idx points to the slot we looked at last. */
949 if (idxp != NULL)
950 *idxp = idx;
951 return (NULL);
952 }
953
954 static prop_object_t
955 _prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked)
956 {
957 const struct _prop_dict_entry *pde;
958 prop_object_t po = NULL;
959
960 if (! prop_object_is_dictionary(pd))
961 return (NULL);
962
963 if (!locked) {
964 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
965 }
966 pde = _prop_dict_lookup(pd, key, NULL);
967 if (pde != NULL) {
968 _PROP_ASSERT(pde->pde_objref != NULL);
969 po = pde->pde_objref;
970 }
971 if (!locked) {
972 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
973 }
974 return (po);
975 }
976 /*
977 * prop_dictionary_get --
978 * Return the object stored with specified key.
979 */
980 _PROP_EXPORT prop_object_t
981 prop_dictionary_get(prop_dictionary_t pd, const char *key)
982 {
983 prop_object_t po = NULL;
984
985 if (! prop_object_is_dictionary(pd))
986 return (NULL);
987
988 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
989 po = _prop_dictionary_get(pd, key, true);
990 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
991 return (po);
992 }
993
994 static prop_object_t
995 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
996 bool locked)
997 {
998
999 if (! (prop_object_is_dictionary(pd) &&
1000 prop_object_is_dictionary_keysym(pdk)))
1001 return (NULL);
1002
1003 return (_prop_dictionary_get(pd, pdk->pdk_key, locked));
1004 }
1005
1006 /*
1007 * prop_dictionary_get_keysym --
1008 * Return the object stored at the location encoded by the keysym.
1009 */
1010 _PROP_EXPORT prop_object_t
1011 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
1012 {
1013
1014 return (_prop_dictionary_get_keysym(pd, pdk, false));
1015 }
1016
1017 /*
1018 * prop_dictionary_set --
1019 * Store a reference to an object at with the specified key.
1020 * If the key already exist, the original object is released.
1021 */
1022 _PROP_EXPORT bool
1023 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
1024 {
1025 struct _prop_dict_entry *pde;
1026 prop_dictionary_keysym_t pdk;
1027 unsigned int idx;
1028 bool rv = false;
1029
1030 if (! prop_object_is_dictionary(pd))
1031 return (false);
1032
1033 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
1034
1035 if (prop_dictionary_is_immutable(pd))
1036 return (false);
1037
1038 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1039
1040 pde = _prop_dict_lookup(pd, key, &idx);
1041 if (pde != NULL) {
1042 prop_object_t opo = pde->pde_objref;
1043 prop_object_retain(po);
1044 pde->pde_objref = po;
1045 prop_object_release(opo);
1046 rv = true;
1047 goto out;
1048 }
1049
1050 pdk = _prop_dict_keysym_alloc(key);
1051 if (pdk == NULL)
1052 goto out;
1053
1054 if (pd->pd_count == pd->pd_capacity &&
1055 _prop_dictionary_expand(pd,
1056 pd->pd_capacity + EXPAND_STEP) == false) {
1057 prop_object_release(pdk);
1058 goto out;
1059 }
1060
1061 /* At this point, the store will succeed. */
1062 prop_object_retain(po);
1063
1064 if (pd->pd_count == 0) {
1065 pd->pd_array[0].pde_key = pdk;
1066 pd->pd_array[0].pde_objref = po;
1067 pd->pd_count++;
1068 pd->pd_version++;
1069 rv = true;
1070 goto out;
1071 }
1072
1073 pde = &pd->pd_array[idx];
1074 _PROP_ASSERT(pde->pde_key != NULL);
1075
1076 if (strcmp(key, pde->pde_key->pdk_key) < 0) {
1077 /*
1078 * key < pdk_key: insert to the left. This is the same as
1079 * inserting to the right, except we decrement the current
1080 * index first.
1081 *
1082 * Because we're unsigned, we have to special case 0
1083 * (grumble).
1084 */
1085 if (idx == 0) {
1086 memmove(&pd->pd_array[1], &pd->pd_array[0],
1087 pd->pd_count * sizeof(*pde));
1088 pd->pd_array[0].pde_key = pdk;
1089 pd->pd_array[0].pde_objref = po;
1090 pd->pd_count++;
1091 pd->pd_version++;
1092 rv = true;
1093 goto out;
1094 }
1095 idx--;
1096 }
1097
1098 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
1099 (pd->pd_count - (idx + 1)) * sizeof(*pde));
1100 pd->pd_array[idx + 1].pde_key = pdk;
1101 pd->pd_array[idx + 1].pde_objref = po;
1102 pd->pd_count++;
1103
1104 pd->pd_version++;
1105
1106 rv = true;
1107
1108 out:
1109 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1110 return (rv);
1111 }
1112
1113 /*
1114 * prop_dictionary_set_keysym --
1115 * Replace the object in the dictionary at the location encoded by
1116 * the keysym.
1117 */
1118 _PROP_EXPORT bool
1119 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
1120 prop_object_t po)
1121 {
1122
1123 if (! (prop_object_is_dictionary(pd) &&
1124 prop_object_is_dictionary_keysym(pdk)))
1125 return (false);
1126
1127 return (prop_dictionary_set(pd, pdk->pdk_key, po));
1128 }
1129
1130 static void
1131 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
1132 unsigned int idx)
1133 {
1134 prop_dictionary_keysym_t pdk = pde->pde_key;
1135 prop_object_t po = pde->pde_objref;
1136
1137 /*
1138 * Dictionary must be WRITE-LOCKED.
1139 */
1140
1141 _PROP_ASSERT(pd->pd_count != 0);
1142 _PROP_ASSERT(idx < pd->pd_count);
1143 _PROP_ASSERT(pde == &pd->pd_array[idx]);
1144
1145 idx++;
1146 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
1147 (pd->pd_count - idx) * sizeof(*pde));
1148 pd->pd_count--;
1149 pd->pd_version++;
1150
1151
1152 prop_object_release(pdk);
1153
1154 prop_object_release(po);
1155 }
1156
1157 /*
1158 * prop_dictionary_remove --
1159 * Remove the reference to an object with the specified key from
1160 * the dictionary.
1161 */
1162 _PROP_EXPORT void
1163 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
1164 {
1165 struct _prop_dict_entry *pde;
1166 unsigned int idx;
1167
1168 if (! prop_object_is_dictionary(pd))
1169 return;
1170
1171 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1172
1173 /* XXX Should this be a _PROP_ASSERT()? */
1174 if (prop_dictionary_is_immutable(pd))
1175 goto out;
1176
1177 pde = _prop_dict_lookup(pd, key, &idx);
1178 /* XXX Should this be a _PROP_ASSERT()? */
1179 if (pde == NULL)
1180 goto out;
1181
1182 _prop_dictionary_remove(pd, pde, idx);
1183 out:
1184 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1185 }
1186
1187 /*
1188 * prop_dictionary_remove_keysym --
1189 * Remove a reference to an object stored in the dictionary at the
1190 * location encoded by the keysym.
1191 */
1192 _PROP_EXPORT void
1193 prop_dictionary_remove_keysym(prop_dictionary_t pd,
1194 prop_dictionary_keysym_t pdk)
1195 {
1196
1197 if (! (prop_object_is_dictionary(pd) &&
1198 prop_object_is_dictionary_keysym(pdk)))
1199 return;
1200
1201 prop_dictionary_remove(pd, pdk->pdk_key);
1202 }
1203
1204 /*
1205 * prop_dictionary_equals --
1206 * Return true if the two dictionaries are equivalent. Note we do a
1207 * by-value comparison of the objects in the dictionary.
1208 */
1209 _PROP_EXPORT bool
1210 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
1211 {
1212 if (!prop_object_is_dictionary(dict1) ||
1213 !prop_object_is_dictionary(dict2))
1214 return (false);
1215
1216 return (prop_object_equals(dict1, dict2));
1217 }
1218
1219 /*
1220 * prop_dictionary_keysym_value --
1221 * Return a reference to the keysym's value.
1222 */
1223 _PROP_EXPORT const char *
1224 prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk)
1225 {
1226
1227 if (! prop_object_is_dictionary_keysym(pdk))
1228 return (NULL);
1229
1230 return (pdk->pdk_key);
1231 }
1232
1233 _PROP_DEPRECATED(prop_dictionary_keysym_cstring_nocopy,
1234 "this program uses prop_dictionary_keysym_cstring_nocopy(), "
1235 "which is deprecated; use prop_dictionary_keysym_value() instead.")
1236 _PROP_EXPORT const char *
1237 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
1238 {
1239
1240 if (! prop_object_is_dictionary_keysym(pdk))
1241 return (NULL);
1242
1243 return (pdk->pdk_key);
1244 }
1245
1246 /*
1247 * prop_dictionary_keysym_equals --
1248 * Return true if the two dictionary key symbols are equivalent.
1249 * Note: We do not compare the object references.
1250 */
1251 _PROP_EXPORT bool
1252 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
1253 prop_dictionary_keysym_t pdk2)
1254 {
1255 if (!prop_object_is_dictionary_keysym(pdk1) ||
1256 !prop_object_is_dictionary_keysym(pdk2))
1257 return (false);
1258
1259 return (prop_object_equals(pdk1, pdk2));
1260 }
1261
1262 /*
1263 * prop_dictionary_externalize --
1264 * Externalize a dictionary in XML format.
1265 */
1266 _PROP_EXPORT char *
1267 prop_dictionary_externalize(prop_dictionary_t pd)
1268 {
1269 return _prop_object_externalize(&pd->pd_obj, PROP_FORMAT_XML);
1270 }
1271
1272 /*
1273 * _prop_dictionary_internalize --
1274 * Parse a <dict>...</dict> and return the object created from the
1275 * external representation.
1276 *
1277 * Internal state in via rec_data is the storage area for the last processed
1278 * key.
1279 * _prop_dictionary_internalize_body is the upper half of the parse loop.
1280 * It is responsible for parsing the key directly and storing it in the area
1281 * referenced by rec_data.
1282 * _prop_dictionary_internalize_cont is the lower half and called with the value
1283 * associated with the key.
1284 */
1285 static bool _prop_dictionary_internalize_body(prop_stack_t,
1286 prop_object_t *, struct _prop_object_internalize_context *, char *);
1287
1288 bool
1289 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
1290 struct _prop_object_internalize_context *ctx)
1291 {
1292 prop_dictionary_t dict;
1293 char *tmpkey;
1294
1295 /* We don't currently understand any attributes. */
1296 if (ctx->poic_tagattr != NULL)
1297 return (true);
1298
1299 dict = prop_dictionary_create();
1300 if (dict == NULL)
1301 return (true);
1302
1303 if (ctx->poic_is_empty_element) {
1304 *obj = dict;
1305 return (true);
1306 }
1307
1308 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1309 if (tmpkey == NULL) {
1310 prop_object_release(dict);
1311 return (true);
1312 }
1313
1314 *obj = dict;
1315 /*
1316 * Opening tag is found, storage for key allocated and
1317 * now continue to the first element.
1318 */
1319 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1320 }
1321
1322 static bool
1323 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
1324 struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
1325 {
1326 prop_dictionary_t dict = *obj;
1327 char *tmpkey = data;
1328
1329 _PROP_ASSERT(tmpkey != NULL);
1330
1331 if (child == NULL ||
1332 prop_dictionary_set(dict, tmpkey, child) == false) {
1333 _PROP_FREE(tmpkey, M_TEMP);
1334 if (child != NULL)
1335 prop_object_release(child);
1336 prop_object_release(dict);
1337 *obj = NULL;
1338 return (true);
1339 }
1340
1341 prop_object_release(child);
1342
1343 /*
1344 * key, value was added, now continue looking for the next key
1345 * or the closing tag. For JSON, we'll skip the comma separator,
1346 * if present.
1347 *
1348 * By doing this here, we correctly error out if a separator
1349 * is found other than after an element, but this does mean
1350 * that we do allow a trailing comma after the final element
1351 * which isn't allowed in the JSON spec, but seems pretty
1352 * harmless (and there are other JSON parsers that also allow
1353 * it).
1354 *
1355 * Conversely, we don't want to *require* the separator if the
1356 * spec doesn't require it, and we don't know what's next in
1357 * the buffer, so we basically treat the separator as completely
1358 * optional. Since there does not appear to be any ambiguity,
1359 * this also seems pretty harmless.
1360 *
1361 * (FWIW, RFC 8259 section 9 seems to specifically allow this.)
1362 */
1363 if (ctx->poic_format == PROP_FORMAT_JSON) {
1364 ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp);
1365 if (*ctx->poic_cp == ',') {
1366 ctx->poic_cp++;
1367 }
1368 }
1369 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1370 }
1371
1372 static bool
1373 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
1374 struct _prop_object_internalize_context *ctx, char *tmpkey)
1375 {
1376 prop_dictionary_t dict = *obj;
1377 size_t keylen;
1378
1379 if (ctx->poic_format == PROP_FORMAT_JSON) {
1380 ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp);
1381
1382 /* Check to see if this is the end of the dictionary. */
1383 if (*ctx->poic_cp == '}') {
1384 /* It is, so don't iterate any further. */
1385 ctx->poic_cp++;
1386 return true;
1387 }
1388
1389 /* It must be the key. */
1390 if (*ctx->poic_cp != '"') {
1391 goto bad;
1392 }
1393 ctx->poic_cp++;
1394
1395 /* Empty keys are not allowed. */
1396 if (*ctx->poic_cp == '"') {
1397 goto bad;
1398 }
1399 } else {
1400 /* Fetch the next tag. */
1401 if (_prop_xml_intern_find_tag(ctx, NULL,
1402 _PROP_TAG_TYPE_EITHER) == false)
1403 goto bad;
1404
1405 /* Check to see if this is the end of the dictionary. */
1406 if (_PROP_TAG_MATCH(ctx, "dict") &&
1407 ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
1408 _PROP_FREE(tmpkey, M_TEMP);
1409 return (true);
1410 }
1411
1412 /* Ok, it must be a non-empty key start tag. */
1413 if (!_PROP_TAG_MATCH(ctx, "key") ||
1414 ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
1415 ctx->poic_is_empty_element)
1416 goto bad;
1417 }
1418
1419 if (_prop_intern_decode_string(ctx, tmpkey, PDK_MAXKEY, &keylen,
1420 &ctx->poic_cp) == false)
1421 goto bad;
1422
1423 _PROP_ASSERT(keylen <= PDK_MAXKEY);
1424 tmpkey[keylen] = '\0';
1425
1426 if (ctx->poic_format == PROP_FORMAT_JSON) {
1427 if (*ctx->poic_cp != '"') {
1428 goto bad;
1429 }
1430 ctx->poic_cp++;
1431
1432 /*
1433 * Next thing we counter needs to be the key/value
1434 * separator.
1435 */
1436 ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp);
1437 if (*ctx->poic_cp != ':') {
1438 goto bad;
1439 }
1440 ctx->poic_cp++;
1441 } else {
1442 if (_prop_xml_intern_find_tag(ctx, "key",
1443 _PROP_TAG_TYPE_END) == false)
1444 goto bad;
1445
1446 /* ..and now the beginning of the value. */
1447 if (_prop_xml_intern_find_tag(ctx, NULL,
1448 _PROP_TAG_TYPE_START) == false)
1449 goto bad;
1450 }
1451
1452 /*
1453 * Key is found, now wait for value to be parsed.
1454 */
1455 if (_prop_stack_push(stack, *obj,
1456 _prop_dictionary_internalize_continue,
1457 tmpkey, NULL))
1458 return (false);
1459
1460 bad:
1461 _PROP_FREE(tmpkey, M_TEMP);
1462 prop_object_release(dict);
1463 *obj = NULL;
1464 return (true);
1465 }
1466
1467 /*
1468 * prop_dictionary_internalize --
1469 * Create a dictionary by parsing the external representation.
1470 */
1471 _PROP_EXPORT prop_dictionary_t
1472 prop_dictionary_internalize(const char *data)
1473 {
1474 return _prop_object_internalize(data, &_prop_dictionary_type_tags);
1475 }
1476
1477 #if !defined(_KERNEL) && !defined(_STANDALONE)
1478 /*
1479 * prop_dictionary_externalize_to_file --
1480 * Externalize a dictionary to the specified file.
1481 */
1482 _PROP_EXPORT bool
1483 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1484 {
1485 return _prop_object_externalize_to_file(&dict->pd_obj, fname,
1486 PROP_FORMAT_XML);
1487 }
1488
1489 /*
1490 * prop_dictionary_internalize_from_file --
1491 * Internalize a dictionary from a file.
1492 */
1493 _PROP_EXPORT prop_dictionary_t
1494 prop_dictionary_internalize_from_file(const char *fname)
1495 {
1496 return _prop_object_internalize_from_file(fname,
1497 &_prop_dictionary_type_tags);
1498 }
1499 #endif /* !_KERNEL && !_STANDALONE */
1500