1 /* $NetBSD: prop_dictionary.c,v 1.33 2008/11/30 00:17:07 haad Exp $ */
4 * Copyright (c) 2010 Juan Romero Pardines (zlib/gzip support).
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
30 * All rights reserved.
32 * This code is derived from software contributed to The NetBSD Foundation
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
44 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
45 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
46 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
47 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
48 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
49 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
50 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
51 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
52 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
53 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
54 * POSSIBILITY OF SUCH DAMAGE.
57 #include <prop/proplib.h>
58 #include "prop_object_impl.h"
59 #include "prop_rb_impl.h"
65 * We implement these like arrays, but we keep them sorted by key.
66 * This allows us to binary-search as well as keep externalized output
67 * sane-looking for human eyes.
70 #define EXPAND_STEP 16
73 * prop_dictionary_keysym_t is allocated with space at the end to hold the
74 * key. This must be a regular object so that we can maintain sane iterator
75 * semantics -- we don't want to require that the caller release the result
76 * of prop_object_iterator_next().
78 * We'd like to have some small'ish keysym objects for up-to-16 characters
79 * in a key, some for up-to-32 characters in a key, and then a final bucket
80 * for up-to-128 characters in a key (not including NUL). Keys longer than
81 * 128 characters are not allowed.
83 struct _prop_dictionary_keysym
{
84 struct _prop_object pdk_obj
;
86 struct rb_node pdk_link
;
88 /* actually variable length */
91 #define RBNODE_TO_PDK(n) \
92 ((struct _prop_dictionary_keysym *) \
93 ((uintptr_t)n - offsetof(struct _prop_dictionary_keysym, pdk_link)))
95 /* pdk_key[1] takes care of the NUL */
96 #define PDK_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16)
97 #define PDK_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32)
98 #define PDK_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128)
100 #define PDK_MAXKEY 128
102 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool
, PDK_SIZE_16
, "pdict16")
103 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool
, PDK_SIZE_32
, "pdict32")
104 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool
, PDK_SIZE_128
, "pdict128")
106 struct _prop_dict_entry
{
107 prop_dictionary_keysym_t pde_key
;
108 prop_object_t pde_objref
;
111 struct _prop_dictionary
{
112 struct _prop_object pd_obj
;
113 _PROP_RWLOCK_DECL(pd_rwlock
)
114 struct _prop_dict_entry
*pd_array
;
115 unsigned int pd_capacity
;
116 unsigned int pd_count
;
122 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */
124 _PROP_POOL_INIT(_prop_dictionary_pool
, sizeof(struct _prop_dictionary
),
126 _PROP_MALLOC_DEFINE(M_PROP_DICT
, "prop dictionary",
127 "property dictionary container object")
129 static _prop_object_free_rv_t
130 _prop_dictionary_free(prop_stack_t
, prop_object_t
*);
131 static void _prop_dictionary_emergency_free(prop_object_t
);
132 static bool _prop_dictionary_externalize(
133 struct _prop_object_externalize_context
*,
135 static _prop_object_equals_rv_t
136 _prop_dictionary_equals(prop_object_t
, prop_object_t
,
138 prop_object_t
*, prop_object_t
*);
139 static void _prop_dictionary_equals_finish(prop_object_t
, prop_object_t
);
140 static prop_object_iterator_t
141 _prop_dictionary_iterator_locked(prop_dictionary_t
);
143 _prop_dictionary_iterator_next_object_locked(void *);
145 _prop_dictionary_get_keysym(prop_dictionary_t
,
146 prop_dictionary_keysym_t
, bool);
148 _prop_dictionary_get(prop_dictionary_t
, const char *, bool);
150 static void _prop_dictionary_lock(void);
151 static void _prop_dictionary_unlock(void);
153 static const struct _prop_object_type _prop_object_type_dictionary
= {
154 .pot_type
= PROP_TYPE_DICTIONARY
,
155 .pot_free
= _prop_dictionary_free
,
156 .pot_emergency_free
= _prop_dictionary_emergency_free
,
157 .pot_extern
= _prop_dictionary_externalize
,
158 .pot_equals
= _prop_dictionary_equals
,
159 .pot_equals_finish
= _prop_dictionary_equals_finish
,
160 .pot_lock
= _prop_dictionary_lock
,
161 .pot_unlock
= _prop_dictionary_unlock
,
164 static _prop_object_free_rv_t
165 _prop_dict_keysym_free(prop_stack_t
, prop_object_t
*);
166 static bool _prop_dict_keysym_externalize(
167 struct _prop_object_externalize_context
*,
169 static _prop_object_equals_rv_t
170 _prop_dict_keysym_equals(prop_object_t
, prop_object_t
,
172 prop_object_t
*, prop_object_t
*);
174 static const struct _prop_object_type _prop_object_type_dict_keysym
= {
175 .pot_type
= PROP_TYPE_DICT_KEYSYM
,
176 .pot_free
= _prop_dict_keysym_free
,
177 .pot_extern
= _prop_dict_keysym_externalize
,
178 .pot_equals
= _prop_dict_keysym_equals
,
181 #define prop_object_is_dictionary(x) \
182 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary)
183 #define prop_object_is_dictionary_keysym(x) \
184 ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym)
186 #define prop_dictionary_is_immutable(x) \
187 (((x)->pd_flags & PD_F_IMMUTABLE) != 0)
189 struct _prop_dictionary_iterator
{
190 struct _prop_object_iterator pdi_base
;
191 unsigned int pdi_index
;
195 * Dictionary key symbols are immutable, and we are likely to have many
196 * duplicated key symbols. So, to save memory, we unique'ify key symbols
197 * so we only have to have one copy of each string.
201 _prop_dict_keysym_rb_compare_nodes(const struct rb_node
*n1
,
202 const struct rb_node
*n2
)
204 const prop_dictionary_keysym_t pdk1
= RBNODE_TO_PDK(n1
);
205 const prop_dictionary_keysym_t pdk2
= RBNODE_TO_PDK(n2
);
207 return (strcmp(pdk1
->pdk_key
, pdk2
->pdk_key
));
211 _prop_dict_keysym_rb_compare_key(const struct rb_node
*n
,
214 const prop_dictionary_keysym_t pdk
= RBNODE_TO_PDK(n
);
217 return (strcmp(pdk
->pdk_key
, cp
));
220 static const struct rb_tree_ops _prop_dict_keysym_rb_tree_ops
= {
221 .rbto_compare_nodes
= _prop_dict_keysym_rb_compare_nodes
,
222 .rbto_compare_key
= _prop_dict_keysym_rb_compare_key
,
225 static struct rb_tree _prop_dict_keysym_tree
;
226 static bool _prop_dict_keysym_tree_initialized
;
228 _PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex
)
231 _prop_dict_keysym_put(prop_dictionary_keysym_t pdk
)
234 if (pdk
->pdk_size
<= PDK_SIZE_16
)
235 _PROP_POOL_PUT(_prop_dictionary_keysym16_pool
, pdk
);
236 else if (pdk
->pdk_size
<= PDK_SIZE_32
)
237 _PROP_POOL_PUT(_prop_dictionary_keysym32_pool
, pdk
);
239 _PROP_ASSERT(pdk
->pdk_size
<= PDK_SIZE_128
);
240 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool
, pdk
);
245 static _prop_object_free_rv_t
246 _prop_dict_keysym_free(prop_stack_t stack
, prop_object_t
*obj
)
248 prop_dictionary_keysym_t pdk
= *obj
;
250 _prop_rb_tree_remove_node(&_prop_dict_keysym_tree
, &pdk
->pdk_link
);
251 _prop_dict_keysym_put(pdk
);
253 return _PROP_OBJECT_FREE_DONE
;
257 _prop_dict_keysym_externalize(struct _prop_object_externalize_context
*ctx
,
260 prop_dictionary_keysym_t pdk
= v
;
262 /* We externalize these as strings, and they're never empty. */
264 _PROP_ASSERT(pdk
->pdk_key
[0] != '\0');
266 if (_prop_object_externalize_start_tag(ctx
, "string") == false ||
267 _prop_object_externalize_append_encoded_cstring(ctx
,
268 pdk
->pdk_key
) == false ||
269 _prop_object_externalize_end_tag(ctx
, "string") == false)
276 static _prop_object_equals_rv_t
277 _prop_dict_keysym_equals(prop_object_t v1
, prop_object_t v2
,
278 void **stored_pointer1
, void **stored_pointer2
,
279 prop_object_t
*next_obj1
, prop_object_t
*next_obj2
)
281 prop_dictionary_keysym_t pdk1
= v1
;
282 prop_dictionary_keysym_t pdk2
= v2
;
285 * There is only ever one copy of a keysym at any given time,
286 * so we can reduce this to a simple pointer equality check.
289 return _PROP_OBJECT_EQUALS_TRUE
;
291 return _PROP_OBJECT_EQUALS_FALSE
;
294 static prop_dictionary_keysym_t
295 _prop_dict_keysym_alloc(const char *key
)
297 prop_dictionary_keysym_t opdk
, pdk
;
298 const struct rb_node
*n
;
303 * Check to see if this already exists in the tree. If it does,
304 * we just retain it and return it.
306 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex
);
307 if (! _prop_dict_keysym_tree_initialized
) {
308 _prop_rb_tree_init(&_prop_dict_keysym_tree
,
309 &_prop_dict_keysym_rb_tree_ops
);
310 _prop_dict_keysym_tree_initialized
= true;
312 n
= _prop_rb_tree_find(&_prop_dict_keysym_tree
, key
);
314 opdk
= RBNODE_TO_PDK(n
);
315 prop_object_retain(opdk
);
316 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex
);
320 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex
);
323 * Not in the tree. Create it now.
326 size
= sizeof(*pdk
) + strlen(key
) /* pdk_key[1] covers the NUL */;
328 if (size
<= PDK_SIZE_16
)
329 pdk
= _PROP_POOL_GET(_prop_dictionary_keysym16_pool
);
330 else if (size
<= PDK_SIZE_32
)
331 pdk
= _PROP_POOL_GET(_prop_dictionary_keysym32_pool
);
332 else if (size
<= PDK_SIZE_128
)
333 pdk
= _PROP_POOL_GET(_prop_dictionary_keysym128_pool
);
335 pdk
= NULL
; /* key too long */
340 _prop_object_init(&pdk
->pdk_obj
, &_prop_object_type_dict_keysym
);
342 strcpy(pdk
->pdk_key
, key
);
343 pdk
->pdk_size
= size
;
346 * We dropped the mutex when we allocated the new object, so
347 * we have to check again if it is in the tree.
349 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex
);
350 n
= _prop_rb_tree_find(&_prop_dict_keysym_tree
, key
);
352 opdk
= RBNODE_TO_PDK(n
);
353 prop_object_retain(opdk
);
354 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex
);
355 _prop_dict_keysym_put(pdk
);
358 rv
= _prop_rb_tree_insert_node(&_prop_dict_keysym_tree
, &pdk
->pdk_link
);
359 _PROP_ASSERT(rv
== true);
360 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex
);
364 static _prop_object_free_rv_t
365 _prop_dictionary_free(prop_stack_t stack
, prop_object_t
*obj
)
367 prop_dictionary_t pd
= *obj
;
368 prop_dictionary_keysym_t pdk
;
371 _PROP_ASSERT(pd
->pd_count
<= pd
->pd_capacity
);
372 _PROP_ASSERT((pd
->pd_capacity
== 0 && pd
->pd_array
== NULL
) ||
373 (pd
->pd_capacity
!= 0 && pd
->pd_array
!= NULL
));
375 /* The empty dictorinary is easy, handle that first. */
376 if (pd
->pd_count
== 0) {
377 if (pd
->pd_array
!= NULL
)
378 _PROP_FREE(pd
->pd_array
, M_PROP_DICT
);
380 _PROP_RWLOCK_DESTROY(pd
->pd_rwlock
);
382 _PROP_POOL_PUT(_prop_dictionary_pool
, pd
);
384 return (_PROP_OBJECT_FREE_DONE
);
387 po
= pd
->pd_array
[pd
->pd_count
- 1].pde_objref
;
388 _PROP_ASSERT(po
!= NULL
);
392 * If we are in emergency release mode,
393 * just let caller recurse down.
396 return (_PROP_OBJECT_FREE_FAILED
);
399 /* Otherwise, try to push the current object on the stack. */
400 if (!_prop_stack_push(stack
, pd
, NULL
, NULL
, NULL
)) {
401 /* Push failed, entering emergency release mode. */
402 return (_PROP_OBJECT_FREE_FAILED
);
404 /* Object pushed on stack, caller will release it. */
406 pdk
= pd
->pd_array
[pd
->pd_count
].pde_key
;
407 _PROP_ASSERT(pdk
!= NULL
);
409 prop_object_release(pdk
);
412 return (_PROP_OBJECT_FREE_RECURSE
);
417 _prop_dictionary_lock(void)
419 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex
);
423 _prop_dictionary_unlock(void)
425 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex
);
429 _prop_dictionary_emergency_free(prop_object_t obj
)
431 prop_dictionary_t pd
= obj
;
432 prop_dictionary_keysym_t pdk
;
434 _PROP_ASSERT(pd
->pd_count
!= 0);
437 pdk
= pd
->pd_array
[pd
->pd_count
].pde_key
;
438 _PROP_ASSERT(pdk
!= NULL
);
439 prop_object_release(pdk
);
443 _prop_dictionary_externalize(struct _prop_object_externalize_context
*ctx
,
446 prop_dictionary_t pd
= v
;
447 prop_dictionary_keysym_t pdk
;
448 struct _prop_object
*po
;
449 prop_object_iterator_t pi
;
453 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
455 if (pd
->pd_count
== 0) {
456 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
457 return (_prop_object_externalize_empty_tag(ctx
, "dict"));
460 if (_prop_object_externalize_start_tag(ctx
, "dict") == false ||
461 _prop_object_externalize_append_char(ctx
, '\n') == false)
464 pi
= _prop_dictionary_iterator_locked(pd
);
469 _PROP_ASSERT(ctx
->poec_depth
!= 0);
471 while ((pdk
= _prop_dictionary_iterator_next_object_locked(pi
))
473 po
= _prop_dictionary_get_keysym(pd
, pdk
, true);
475 _prop_object_externalize_start_tag(ctx
, "key") == false ||
476 _prop_object_externalize_append_encoded_cstring(ctx
,
477 pdk
->pdk_key
) == false ||
478 _prop_object_externalize_end_tag(ctx
, "key") == false ||
479 (*po
->po_type
->pot_extern
)(ctx
, po
) == false) {
480 prop_object_iterator_release(pi
);
485 prop_object_iterator_release(pi
);
488 for (i
= 0; i
< ctx
->poec_depth
; i
++) {
489 if (_prop_object_externalize_append_char(ctx
, '\t') == false)
492 if (_prop_object_externalize_end_tag(ctx
, "dict") == false)
498 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
503 static _prop_object_equals_rv_t
504 _prop_dictionary_equals(prop_object_t v1
, prop_object_t v2
,
505 void **stored_pointer1
, void **stored_pointer2
,
506 prop_object_t
*next_obj1
, prop_object_t
*next_obj2
)
508 prop_dictionary_t dict1
= v1
;
509 prop_dictionary_t dict2
= v2
;
511 _prop_object_equals_rv_t rv
= _PROP_OBJECT_EQUALS_FALSE
;
514 return (_PROP_OBJECT_EQUALS_TRUE
);
516 _PROP_ASSERT(*stored_pointer1
== *stored_pointer2
);
518 idx
= (uintptr_t)*stored_pointer1
;
521 if ((uintptr_t)dict1
< (uintptr_t)dict2
) {
522 _PROP_RWLOCK_RDLOCK(dict1
->pd_rwlock
);
523 _PROP_RWLOCK_RDLOCK(dict2
->pd_rwlock
);
525 _PROP_RWLOCK_RDLOCK(dict2
->pd_rwlock
);
526 _PROP_RWLOCK_RDLOCK(dict1
->pd_rwlock
);
530 if (dict1
->pd_count
!= dict2
->pd_count
)
533 if (idx
== dict1
->pd_count
) {
534 rv
= _PROP_OBJECT_EQUALS_TRUE
;
538 _PROP_ASSERT(idx
< dict1
->pd_count
);
540 *stored_pointer1
= (void *)(idx
+ 1);
541 *stored_pointer2
= (void *)(idx
+ 1);
543 *next_obj1
= &dict1
->pd_array
[idx
].pde_objref
;
544 *next_obj2
= &dict2
->pd_array
[idx
].pde_objref
;
546 if (!prop_dictionary_keysym_equals(dict1
->pd_array
[idx
].pde_key
,
547 dict2
->pd_array
[idx
].pde_key
))
550 return (_PROP_OBJECT_EQUALS_RECURSE
);
553 _PROP_RWLOCK_UNLOCK(dict1
->pd_rwlock
);
554 _PROP_RWLOCK_UNLOCK(dict2
->pd_rwlock
);
559 _prop_dictionary_equals_finish(prop_object_t v1
, prop_object_t v2
)
561 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t
)v1
)->pd_rwlock
);
562 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t
)v2
)->pd_rwlock
);
565 static prop_dictionary_t
566 _prop_dictionary_alloc(unsigned int capacity
)
568 prop_dictionary_t pd
;
569 struct _prop_dict_entry
*array
;
572 array
= _PROP_CALLOC(capacity
* sizeof(*array
), M_PROP_DICT
);
578 pd
= _PROP_POOL_GET(_prop_dictionary_pool
);
580 _prop_object_init(&pd
->pd_obj
, &_prop_object_type_dictionary
);
582 _PROP_RWLOCK_INIT(pd
->pd_rwlock
);
583 pd
->pd_array
= array
;
584 pd
->pd_capacity
= capacity
;
589 } else if (array
!= NULL
)
590 _PROP_FREE(array
, M_PROP_DICT
);
596 _prop_dictionary_expand(prop_dictionary_t pd
, unsigned int capacity
)
598 struct _prop_dict_entry
*array
, *oarray
;
601 * Dictionary must be WRITE-LOCKED.
604 oarray
= pd
->pd_array
;
606 array
= _PROP_CALLOC(capacity
* sizeof(*array
), M_PROP_DICT
);
610 memcpy(array
, oarray
, pd
->pd_capacity
* sizeof(*array
));
611 pd
->pd_array
= array
;
612 pd
->pd_capacity
= capacity
;
615 _PROP_FREE(oarray
, M_PROP_DICT
);
621 _prop_dictionary_iterator_next_object_locked(void *v
)
623 struct _prop_dictionary_iterator
*pdi
= v
;
624 prop_dictionary_t pd
= pdi
->pdi_base
.pi_obj
;
625 prop_dictionary_keysym_t pdk
= NULL
;
627 _PROP_ASSERT(prop_object_is_dictionary(pd
));
629 if (pd
->pd_version
!= pdi
->pdi_base
.pi_version
)
630 goto out
; /* dictionary changed during iteration */
632 _PROP_ASSERT(pdi
->pdi_index
<= pd
->pd_count
);
634 if (pdi
->pdi_index
== pd
->pd_count
)
635 goto out
; /* we've iterated all objects */
637 pdk
= pd
->pd_array
[pdi
->pdi_index
].pde_key
;
645 _prop_dictionary_iterator_next_object(void *v
)
647 struct _prop_dictionary_iterator
*pdi
= v
;
648 prop_dictionary_t pd
= pdi
->pdi_base
.pi_obj
;
649 prop_dictionary_keysym_t pdk
;
651 _PROP_ASSERT(prop_object_is_dictionary(pd
));
653 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
654 pdk
= _prop_dictionary_iterator_next_object_locked(pdi
);
655 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
660 _prop_dictionary_iterator_reset_locked(void *v
)
662 struct _prop_dictionary_iterator
*pdi
= v
;
663 prop_dictionary_t pd
= pdi
->pdi_base
.pi_obj
;
665 _PROP_ASSERT(prop_object_is_dictionary(pd
));
668 pdi
->pdi_base
.pi_version
= pd
->pd_version
;
672 _prop_dictionary_iterator_reset(void *v
)
674 struct _prop_dictionary_iterator
*pdi
= v
;
675 prop_dictionary_t pd
= pdi
->pdi_base
.pi_obj
;
677 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
678 _prop_dictionary_iterator_reset_locked(pdi
);
679 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
683 * prop_dictionary_create --
684 * Create a dictionary.
687 prop_dictionary_create(void)
690 return (_prop_dictionary_alloc(0));
694 * prop_dictionary_create_with_capacity --
695 * Create a dictionary with the capacity to store N objects.
698 prop_dictionary_create_with_capacity(unsigned int capacity
)
701 return (_prop_dictionary_alloc(capacity
));
705 * prop_dictionary_copy --
706 * Copy a dictionary. The new dictionary has an initial capacity equal
707 * to the number of objects stored int the original dictionary. The new
708 * dictionary contains refrences to the original dictionary's objects,
709 * not copies of those objects (i.e. a shallow copy).
712 prop_dictionary_copy(prop_dictionary_t opd
)
714 prop_dictionary_t pd
;
715 prop_dictionary_keysym_t pdk
;
719 if (! prop_object_is_dictionary(opd
))
722 _PROP_RWLOCK_RDLOCK(opd
->pd_rwlock
);
724 pd
= _prop_dictionary_alloc(opd
->pd_count
);
726 for (idx
= 0; idx
< opd
->pd_count
; idx
++) {
727 pdk
= opd
->pd_array
[idx
].pde_key
;
728 po
= opd
->pd_array
[idx
].pde_objref
;
730 prop_object_retain(pdk
);
731 prop_object_retain(po
);
733 pd
->pd_array
[idx
].pde_key
= pdk
;
734 pd
->pd_array
[idx
].pde_objref
= po
;
736 pd
->pd_count
= opd
->pd_count
;
737 pd
->pd_flags
= opd
->pd_flags
;
739 _PROP_RWLOCK_UNLOCK(opd
->pd_rwlock
);
744 * prop_dictionary_copy_mutable --
745 * Like prop_dictionary_copy(), but the resulting dictionary is
749 prop_dictionary_copy_mutable(prop_dictionary_t opd
)
751 prop_dictionary_t pd
;
753 if (! prop_object_is_dictionary(opd
))
756 pd
= prop_dictionary_copy(opd
);
758 pd
->pd_flags
&= ~PD_F_IMMUTABLE
;
764 * prop_dictionary_make_immutable --
765 * Set the immutable flag on that dictionary.
768 prop_dictionary_make_immutable(prop_dictionary_t pd
)
771 _PROP_RWLOCK_WRLOCK(pd
->pd_rwlock
);
772 if (prop_dictionary_is_immutable(pd
) == false)
773 pd
->pd_flags
|= PD_F_IMMUTABLE
;
774 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
778 * prop_dictionary_count --
779 * Return the number of objects stored in the dictionary.
782 prop_dictionary_count(prop_dictionary_t pd
)
786 if (! prop_object_is_dictionary(pd
))
789 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
791 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
797 * prop_dictionary_ensure_capacity --
798 * Ensure that the dictionary has the capacity to store the specified
799 * total number of objects (including the objects already stored in
803 prop_dictionary_ensure_capacity(prop_dictionary_t pd
, unsigned int capacity
)
807 if (! prop_object_is_dictionary(pd
))
810 _PROP_RWLOCK_WRLOCK(pd
->pd_rwlock
);
811 if (capacity
> pd
->pd_capacity
)
812 rv
= _prop_dictionary_expand(pd
, capacity
);
815 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
819 static prop_object_iterator_t
820 _prop_dictionary_iterator_locked(prop_dictionary_t pd
)
822 struct _prop_dictionary_iterator
*pdi
;
824 if (! prop_object_is_dictionary(pd
))
827 pdi
= _PROP_CALLOC(sizeof(*pdi
), M_TEMP
);
830 pdi
->pdi_base
.pi_next_object
= _prop_dictionary_iterator_next_object
;
831 pdi
->pdi_base
.pi_reset
= _prop_dictionary_iterator_reset
;
832 prop_object_retain(pd
);
833 pdi
->pdi_base
.pi_obj
= pd
;
834 _prop_dictionary_iterator_reset_locked(pdi
);
836 return (&pdi
->pdi_base
);
840 * prop_dictionary_iterator --
841 * Return an iterator for the dictionary. The dictionary is retained by
844 prop_object_iterator_t
845 prop_dictionary_iterator(prop_dictionary_t pd
)
847 prop_object_iterator_t pi
;
849 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
850 pi
= _prop_dictionary_iterator_locked(pd
);
851 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
856 * prop_dictionary_all_keys --
857 * Return an array containing a snapshot of all of the keys
861 prop_dictionary_all_keys(prop_dictionary_t pd
)
867 if (! prop_object_is_dictionary(pd
))
870 /* There is no pressing need to lock the dictionary for this. */
871 array
= prop_array_create_with_capacity(pd
->pd_count
);
873 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
875 for (idx
= 0; idx
< pd
->pd_count
; idx
++) {
876 rv
= prop_array_add(array
, pd
->pd_array
[idx
].pde_key
);
881 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
884 prop_object_release(array
);
890 static struct _prop_dict_entry
*
891 _prop_dict_lookup(prop_dictionary_t pd
, const char *key
,
894 struct _prop_dict_entry
*pde
;
895 unsigned int base
, idx
, distance
;
899 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
902 for (idx
= 0, base
= 0, distance
= pd
->pd_count
; distance
!= 0;
904 idx
= base
+ (distance
>> 1);
905 pde
= &pd
->pd_array
[idx
];
906 _PROP_ASSERT(pde
->pde_key
!= NULL
);
907 res
= strcmp(key
, pde
->pde_key
->pdk_key
);
913 if (res
> 0) { /* key > pdk_key: move right */
916 } /* else move left */
919 /* idx points to the slot we looked at last. */
926 _prop_dictionary_get(prop_dictionary_t pd
, const char *key
, bool locked
)
928 const struct _prop_dict_entry
*pde
;
929 prop_object_t po
= NULL
;
931 if (! prop_object_is_dictionary(pd
))
935 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
936 pde
= _prop_dict_lookup(pd
, key
, NULL
);
938 _PROP_ASSERT(pde
->pde_objref
!= NULL
);
939 po
= pde
->pde_objref
;
942 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
946 * prop_dictionary_get --
947 * Return the object stored with specified key.
950 prop_dictionary_get(prop_dictionary_t pd
, const char *key
)
954 _PROP_RWLOCK_RDLOCK(pd
->pd_rwlock
);
955 po
= _prop_dictionary_get(pd
, key
, true);
956 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
961 _prop_dictionary_get_keysym(prop_dictionary_t pd
, prop_dictionary_keysym_t pdk
,
965 if (! (prop_object_is_dictionary(pd
) &&
966 prop_object_is_dictionary_keysym(pdk
)))
969 return (_prop_dictionary_get(pd
, pdk
->pdk_key
, locked
));
973 * prop_dictionary_get_keysym --
974 * Return the object stored at the location encoded by the keysym.
977 prop_dictionary_get_keysym(prop_dictionary_t pd
, prop_dictionary_keysym_t pdk
)
980 return (_prop_dictionary_get_keysym(pd
, pdk
, false));
984 * prop_dictionary_set --
985 * Store a reference to an object at with the specified key.
986 * If the key already exisit, the original object is released.
989 prop_dictionary_set(prop_dictionary_t pd
, const char *key
, prop_object_t po
)
991 struct _prop_dict_entry
*pde
;
992 prop_dictionary_keysym_t pdk
;
996 if (! prop_object_is_dictionary(pd
))
999 _PROP_ASSERT(pd
->pd_count
<= pd
->pd_capacity
);
1001 if (prop_dictionary_is_immutable(pd
))
1004 _PROP_RWLOCK_WRLOCK(pd
->pd_rwlock
);
1006 pde
= _prop_dict_lookup(pd
, key
, &idx
);
1008 prop_object_t opo
= pde
->pde_objref
;
1009 prop_object_retain(po
);
1010 pde
->pde_objref
= po
;
1011 prop_object_release(opo
);
1016 pdk
= _prop_dict_keysym_alloc(key
);
1020 if (pd
->pd_count
== pd
->pd_capacity
&&
1021 _prop_dictionary_expand(pd
,
1022 pd
->pd_capacity
+ EXPAND_STEP
) == false) {
1023 prop_object_release(pdk
);
1027 /* At this point, the store will succeed. */
1028 prop_object_retain(po
);
1030 if (pd
->pd_count
== 0) {
1031 pd
->pd_array
[0].pde_key
= pdk
;
1032 pd
->pd_array
[0].pde_objref
= po
;
1039 pde
= &pd
->pd_array
[idx
];
1040 _PROP_ASSERT(pde
->pde_key
!= NULL
);
1042 if (strcmp(key
, pde
->pde_key
->pdk_key
) < 0) {
1044 * key < pdk_key: insert to the left. This is the same as
1045 * inserting to the right, except we decrement the current
1048 * Because we're unsigned, we have to special case 0
1052 memmove(&pd
->pd_array
[1], &pd
->pd_array
[0],
1053 pd
->pd_count
* sizeof(*pde
));
1054 pd
->pd_array
[0].pde_key
= pdk
;
1055 pd
->pd_array
[0].pde_objref
= po
;
1064 memmove(&pd
->pd_array
[idx
+ 2], &pd
->pd_array
[idx
+ 1],
1065 (pd
->pd_count
- (idx
+ 1)) * sizeof(*pde
));
1066 pd
->pd_array
[idx
+ 1].pde_key
= pdk
;
1067 pd
->pd_array
[idx
+ 1].pde_objref
= po
;
1075 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
1080 * prop_dictionary_set_keysym --
1081 * Replace the object in the dictionary at the location encoded by
1085 prop_dictionary_set_keysym(prop_dictionary_t pd
, prop_dictionary_keysym_t pdk
,
1089 if (! (prop_object_is_dictionary(pd
) &&
1090 prop_object_is_dictionary_keysym(pdk
)))
1093 return (prop_dictionary_set(pd
, pdk
->pdk_key
, po
));
1097 _prop_dictionary_remove(prop_dictionary_t pd
, struct _prop_dict_entry
*pde
,
1100 prop_dictionary_keysym_t pdk
= pde
->pde_key
;
1101 prop_object_t po
= pde
->pde_objref
;
1104 * Dictionary must be WRITE-LOCKED.
1107 _PROP_ASSERT(pd
->pd_count
!= 0);
1108 _PROP_ASSERT(idx
< pd
->pd_count
);
1109 _PROP_ASSERT(pde
== &pd
->pd_array
[idx
]);
1112 memmove(&pd
->pd_array
[idx
- 1], &pd
->pd_array
[idx
],
1113 (pd
->pd_count
- idx
) * sizeof(*pde
));
1118 prop_object_release(pdk
);
1120 prop_object_release(po
);
1124 * prop_dictionary_remove --
1125 * Remove the reference to an object with the specified key from
1129 prop_dictionary_remove(prop_dictionary_t pd
, const char *key
)
1131 struct _prop_dict_entry
*pde
;
1134 if (! prop_object_is_dictionary(pd
))
1137 _PROP_RWLOCK_WRLOCK(pd
->pd_rwlock
);
1139 /* XXX Should this be a _PROP_ASSERT()? */
1140 if (prop_dictionary_is_immutable(pd
))
1143 pde
= _prop_dict_lookup(pd
, key
, &idx
);
1144 /* XXX Should this be a _PROP_ASSERT()? */
1148 _prop_dictionary_remove(pd
, pde
, idx
);
1150 _PROP_RWLOCK_UNLOCK(pd
->pd_rwlock
);
1154 * prop_dictionary_remove_keysym --
1155 * Remove a reference to an object stored in the dictionary at the
1156 * location encoded by the keysym.
1159 prop_dictionary_remove_keysym(prop_dictionary_t pd
,
1160 prop_dictionary_keysym_t pdk
)
1163 if (! (prop_object_is_dictionary(pd
) &&
1164 prop_object_is_dictionary_keysym(pdk
)))
1167 prop_dictionary_remove(pd
, pdk
->pdk_key
);
1171 * prop_dictionary_equals --
1172 * Return true if the two dictionaries are equivalent. Note we do a
1173 * by-value comparison of the objects in the dictionary.
1176 prop_dictionary_equals(prop_dictionary_t dict1
, prop_dictionary_t dict2
)
1178 if (!prop_object_is_dictionary(dict1
) ||
1179 !prop_object_is_dictionary(dict2
))
1182 return (prop_object_equals(dict1
, dict2
));
1186 * prop_dictionary_keysym_cstring_nocopy --
1187 * Return an immutable reference to the keysym's value.
1190 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk
)
1193 if (! prop_object_is_dictionary_keysym(pdk
))
1196 return (pdk
->pdk_key
);
1200 * prop_dictionary_keysym_equals --
1201 * Return true if the two dictionary key symbols are equivalent.
1202 * Note: We do not compare the object references.
1205 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1
,
1206 prop_dictionary_keysym_t pdk2
)
1208 if (!prop_object_is_dictionary_keysym(pdk1
) ||
1209 !prop_object_is_dictionary_keysym(pdk2
))
1212 return (prop_object_equals(pdk1
, pdk2
));
1216 * prop_dictionary_externalize --
1217 * Externalize a dictionary, returning a NUL-terminated buffer
1218 * containing the XML-style representation. The buffer is allocated
1219 * with the M_TEMP memory type.
1222 prop_dictionary_externalize(prop_dictionary_t pd
)
1224 struct _prop_object_externalize_context
*ctx
;
1227 ctx
= _prop_object_externalize_context_alloc();
1231 if (_prop_object_externalize_header(ctx
) == false ||
1232 (*pd
->pd_obj
.po_type
->pot_extern
)(ctx
, pd
) == false ||
1233 _prop_object_externalize_footer(ctx
) == false) {
1234 /* We are responsible for releasing the buffer. */
1235 _PROP_FREE(ctx
->poec_buf
, M_TEMP
);
1236 _prop_object_externalize_context_free(ctx
);
1241 _prop_object_externalize_context_free(ctx
);
1247 * _prop_dictionary_internalize --
1248 * Parse a <dict>...</dict> and return the object created from the
1249 * external representation.
1251 * Internal state in via rec_data is the storage area for the last processed
1253 * _prop_dictionary_internalize_body is the upper half of the parse loop.
1254 * It is responsible for parsing the key directly and storing it in the area
1255 * referenced by rec_data.
1256 * _prop_dictionary_internalize_cont is the lower half and called with the value
1257 * associated with the key.
1259 static bool _prop_dictionary_internalize_body(prop_stack_t
,
1260 prop_object_t
*, struct _prop_object_internalize_context
*, char *);
1263 _prop_dictionary_internalize(prop_stack_t stack
, prop_object_t
*obj
,
1264 struct _prop_object_internalize_context
*ctx
)
1266 prop_dictionary_t dict
;
1269 /* We don't currently understand any attributes. */
1270 if (ctx
->poic_tagattr
!= NULL
)
1273 dict
= prop_dictionary_create();
1277 if (ctx
->poic_is_empty_element
) {
1282 tmpkey
= _PROP_MALLOC(PDK_MAXKEY
+ 1, M_TEMP
);
1283 if (tmpkey
== NULL
) {
1284 prop_object_release(dict
);
1290 * Opening tag is found, storage for key allocated and
1291 * now continue to the first element.
1293 return _prop_dictionary_internalize_body(stack
, obj
, ctx
, tmpkey
);
1297 _prop_dictionary_internalize_continue(prop_stack_t stack
, prop_object_t
*obj
,
1298 struct _prop_object_internalize_context
*ctx
, void *data
, prop_object_t child
)
1300 prop_dictionary_t dict
= *obj
;
1301 char *tmpkey
= data
;
1303 _PROP_ASSERT(tmpkey
!= NULL
);
1305 if (child
== NULL
||
1306 prop_dictionary_set(dict
, tmpkey
, child
) == false) {
1307 _PROP_FREE(tmpkey
, M_TEMP
);
1309 prop_object_release(child
);
1310 prop_object_release(dict
);
1315 prop_object_release(child
);
1318 * key, value was added, now continue looking for the next key
1319 * or the closing tag.
1321 return _prop_dictionary_internalize_body(stack
, obj
, ctx
, tmpkey
);
1325 _prop_dictionary_internalize_body(prop_stack_t stack
, prop_object_t
*obj
,
1326 struct _prop_object_internalize_context
*ctx
, char *tmpkey
)
1328 prop_dictionary_t dict
= *obj
;
1331 /* Fetch the next tag. */
1332 if (_prop_object_internalize_find_tag(ctx
, NULL
, _PROP_TAG_TYPE_EITHER
) == false)
1335 /* Check to see if this is the end of the dictionary. */
1336 if (_PROP_TAG_MATCH(ctx
, "dict") &&
1337 ctx
->poic_tag_type
== _PROP_TAG_TYPE_END
) {
1338 _PROP_FREE(tmpkey
, M_TEMP
);
1342 /* Ok, it must be a non-empty key start tag. */
1343 if (!_PROP_TAG_MATCH(ctx
, "key") ||
1344 ctx
->poic_tag_type
!= _PROP_TAG_TYPE_START
||
1345 ctx
->poic_is_empty_element
)
1348 if (_prop_object_internalize_decode_string(ctx
,
1349 tmpkey
, PDK_MAXKEY
, &keylen
,
1350 &ctx
->poic_cp
) == false)
1353 _PROP_ASSERT(keylen
<= PDK_MAXKEY
);
1354 tmpkey
[keylen
] = '\0';
1356 if (_prop_object_internalize_find_tag(ctx
, "key",
1357 _PROP_TAG_TYPE_END
) == false)
1360 /* ..and now the beginning of the value. */
1361 if (_prop_object_internalize_find_tag(ctx
, NULL
,
1362 _PROP_TAG_TYPE_START
) == false)
1366 * Key is found, now wait for value to be parsed.
1368 if (_prop_stack_push(stack
, *obj
,
1369 _prop_dictionary_internalize_continue
,
1374 _PROP_FREE(tmpkey
, M_TEMP
);
1375 prop_object_release(dict
);
1381 * prop_dictionary_internalize --
1382 * Create a dictionary by parsing the NUL-terminated XML-style
1386 prop_dictionary_internalize(const char *xml
)
1388 return _prop_generic_internalize(xml
, "dict");
1392 * prop_dictionary_externalize_to_file --
1393 * Externalize a dictionary to the specified file.
1396 prop_dictionary_externalize_to_file(prop_dictionary_t dict
, const char *fname
)
1400 int save_errno
= 0; /* XXXGCC -Wuninitialized [mips, ...] */
1402 xml
= prop_dictionary_externalize(dict
);
1405 rv
= _prop_object_externalize_write_file(fname
, xml
,
1406 strlen(xml
), false);
1409 _PROP_FREE(xml
, M_TEMP
);
1417 * prop_dictionary_externalize_to_zfile ---
1418 * Externalize a dictionary to the specified file and on the fly
1419 * compressing the result with gzip (via zlib).
1422 prop_dictionary_externalize_to_zfile(prop_dictionary_t dict
, const char *fname
)
1428 xml
= prop_dictionary_externalize(dict
);
1431 rv
= _prop_object_externalize_write_file(fname
, xml
, strlen(xml
), true);
1434 _PROP_FREE(xml
, M_TEMP
);
1442 * prop_dictionary_internalize_from_file --
1443 * Internalize a dictionary from a file.
1446 prop_dictionary_internalize_from_file(const char *fname
)
1448 struct _prop_object_internalize_mapped_file
*mf
;
1449 prop_dictionary_t dict
;
1451 mf
= _prop_object_internalize_map_file(fname
);
1454 dict
= prop_dictionary_internalize(mf
->poimf_xml
);
1455 _prop_object_internalize_unmap_file(mf
);
1460 #define _READ_CHUNK 512
1462 * prop_dictionary_internalize_from_zfile ---
1463 * Internalize a dictionary from a compressed gzip file.
1466 prop_dictionary_internalize_from_zfile(const char *fname
)
1468 struct _prop_object_internalize_mapped_file
*mf
;
1469 prop_dictionary_t dict
;
1471 unsigned char out
[_READ_CHUNK
];
1472 char *uncomp_xml
= NULL
;
1474 ssize_t totalsize
= 0;
1477 mf
= _prop_object_internalize_map_file(fname
);
1481 /* Decompress the mmap'ed buffer with zlib */
1482 strm
.zalloc
= Z_NULL
;
1483 strm
.zfree
= Z_NULL
;
1484 strm
.opaque
= Z_NULL
;
1486 strm
.next_in
= Z_NULL
;
1488 /* 15+16 to use gzip method */
1489 if (inflateInit2(&strm
, 15+16) != Z_OK
) {
1490 _prop_object_internalize_unmap_file(mf
);
1493 strm
.avail_in
= mf
->poimf_mapsize
;
1494 strm
.next_in
= mf
->poimf_xml
;
1496 /* Output buffer (uncompressed) */
1497 uncomp_xml
= _PROP_MALLOC(_READ_CHUNK
, M_TEMP
);
1498 if (uncomp_xml
== NULL
) {
1499 (void)inflateEnd(&strm
);
1500 _prop_object_internalize_unmap_file(mf
);
1504 /* Inflate the input buffer and copy into 'uncomp_xml' */
1506 strm
.avail_out
= _READ_CHUNK
;
1507 strm
.next_out
= out
;
1508 rv
= inflate(&strm
, Z_NO_FLUSH
);
1512 * Wrong compressed data or uncompressed, try
1513 * normal method as last resort.
1515 (void)inflateEnd(&strm
);
1516 _PROP_FREE(uncomp_xml
, M_TEMP
);
1517 dict
= prop_dictionary_internalize(mf
->poimf_xml
);
1518 _prop_object_internalize_unmap_file(mf
);
1520 case Z_STREAM_ERROR
:
1523 (void)inflateEnd(&strm
);
1524 _PROP_FREE(uncomp_xml
, M_TEMP
);
1525 _prop_object_internalize_unmap_file(mf
);
1529 have
= _READ_CHUNK
- strm
.avail_out
;
1531 uncomp_xml
= _PROP_REALLOC(uncomp_xml
, totalsize
, M_TEMP
);
1532 memcpy(uncomp_xml
+ totalsize
- have
, out
, have
);
1533 } while (strm
.avail_out
== 0);
1536 (void)inflateEnd(&strm
);
1537 dict
= prop_dictionary_internalize(uncomp_xml
);
1538 _PROP_FREE(uncomp_xml
, M_TEMP
);
1539 _prop_object_internalize_unmap_file(mf
);