Try normal method (uncompressed) if Z_DATA_ERROR is returned.
[portableproplib.git] / src / prop_dictionary.c
blob9a33c3e42175def42dbbe8730709f20a4f20f362
1 /* $NetBSD: prop_dictionary.c,v 1.33 2008/11/30 00:17:07 haad Exp $ */
3 /*-
4 * Copyright (c) 2010 Juan Romero Pardines (zlib/gzip support).
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
28 /*-
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
33 * by Jason R. Thorpe.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
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"
61 #include <errno.h>
62 #include <zlib.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;
85 size_t pdk_size;
86 struct rb_node pdk_link;
87 char pdk_key[1];
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;
117 int pd_flags;
119 uint32_t pd_version;
122 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */
124 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
125 "propdict")
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 *,
134 void *);
135 static _prop_object_equals_rv_t
136 _prop_dictionary_equals(prop_object_t, prop_object_t,
137 void **, void **,
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);
142 static prop_object_t
143 _prop_dictionary_iterator_next_object_locked(void *);
144 static prop_object_t
145 _prop_dictionary_get_keysym(prop_dictionary_t,
146 prop_dictionary_keysym_t, bool);
147 static prop_object_t
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 *,
168 void *);
169 static _prop_object_equals_rv_t
170 _prop_dict_keysym_equals(prop_object_t, prop_object_t,
171 void **, void **,
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.
200 static int
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));
210 static int
211 _prop_dict_keysym_rb_compare_key(const struct rb_node *n,
212 const void *v)
214 const prop_dictionary_keysym_t pdk = RBNODE_TO_PDK(n);
215 const char *cp = v;
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)
230 static void
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);
238 else {
239 _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128);
240 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk);
244 /* ARGSUSED */
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;
256 static bool
257 _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
258 void *v)
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)
270 return (false);
272 return (true);
275 /* ARGSUSED */
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.
288 if (pdk1 == pdk2)
289 return _PROP_OBJECT_EQUALS_TRUE;
290 else
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;
299 size_t size;
300 bool rv;
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;
311 } else {
312 n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
313 if (n != NULL) {
314 opdk = RBNODE_TO_PDK(n);
315 prop_object_retain(opdk);
316 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
317 return (opdk);
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);
334 else
335 pdk = NULL; /* key too long */
337 if (pdk == NULL)
338 return (NULL);
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);
351 if (n != NULL) {
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);
356 return (opdk);
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);
361 return (pdk);
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;
369 prop_object_t po;
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);
390 if (stack == NULL) {
392 * If we are in emergency release mode,
393 * just let caller recurse down.
395 *obj = po;
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. */
405 --pd->pd_count;
406 pdk = pd->pd_array[pd->pd_count].pde_key;
407 _PROP_ASSERT(pdk != NULL);
409 prop_object_release(pdk);
411 *obj = po;
412 return (_PROP_OBJECT_FREE_RECURSE);
416 static void
417 _prop_dictionary_lock(void)
419 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
422 static void
423 _prop_dictionary_unlock(void)
425 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
428 static void
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);
435 --pd->pd_count;
437 pdk = pd->pd_array[pd->pd_count].pde_key;
438 _PROP_ASSERT(pdk != NULL);
439 prop_object_release(pdk);
442 static bool
443 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
444 void *v)
446 prop_dictionary_t pd = v;
447 prop_dictionary_keysym_t pdk;
448 struct _prop_object *po;
449 prop_object_iterator_t pi;
450 unsigned int i;
451 bool rv = false;
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)
462 goto out;
464 pi = _prop_dictionary_iterator_locked(pd);
465 if (pi == NULL)
466 goto out;
468 ctx->poec_depth++;
469 _PROP_ASSERT(ctx->poec_depth != 0);
471 while ((pdk = _prop_dictionary_iterator_next_object_locked(pi))
472 != NULL) {
473 po = _prop_dictionary_get_keysym(pd, pdk, true);
474 if (po == NULL ||
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);
481 goto out;
485 prop_object_iterator_release(pi);
487 ctx->poec_depth--;
488 for (i = 0; i < ctx->poec_depth; i++) {
489 if (_prop_object_externalize_append_char(ctx, '\t') == false)
490 goto out;
492 if (_prop_object_externalize_end_tag(ctx, "dict") == false)
493 goto out;
495 rv = true;
497 out:
498 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
499 return (rv);
502 /* ARGSUSED */
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;
510 uintptr_t idx;
511 _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE;
513 if (dict1 == dict2)
514 return (_PROP_OBJECT_EQUALS_TRUE);
516 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
518 idx = (uintptr_t)*stored_pointer1;
520 if (idx == 0) {
521 if ((uintptr_t)dict1 < (uintptr_t)dict2) {
522 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
523 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
524 } else {
525 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
526 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
530 if (dict1->pd_count != dict2->pd_count)
531 goto out;
533 if (idx == dict1->pd_count) {
534 rv = _PROP_OBJECT_EQUALS_TRUE;
535 goto out;
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))
548 goto out;
550 return (_PROP_OBJECT_EQUALS_RECURSE);
552 out:
553 _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
554 _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
555 return (rv);
558 static void
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;
571 if (capacity != 0) {
572 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
573 if (array == NULL)
574 return (NULL);
575 } else
576 array = NULL;
578 pd = _PROP_POOL_GET(_prop_dictionary_pool);
579 if (pd != NULL) {
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;
585 pd->pd_count = 0;
586 pd->pd_flags = 0;
588 pd->pd_version = 0;
589 } else if (array != NULL)
590 _PROP_FREE(array, M_PROP_DICT);
592 return (pd);
595 static bool
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);
607 if (array == NULL)
608 return (false);
609 if (oarray != NULL)
610 memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
611 pd->pd_array = array;
612 pd->pd_capacity = capacity;
614 if (oarray != NULL)
615 _PROP_FREE(oarray, M_PROP_DICT);
617 return (true);
620 static prop_object_t
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;
638 pdi->pdi_index++;
640 out:
641 return (pdk);
644 static prop_object_t
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);
656 return (pdk);
659 static void
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));
667 pdi->pdi_index = 0;
668 pdi->pdi_base.pi_version = pd->pd_version;
671 static void
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.
686 prop_dictionary_t
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.
697 prop_dictionary_t
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).
711 prop_dictionary_t
712 prop_dictionary_copy(prop_dictionary_t opd)
714 prop_dictionary_t pd;
715 prop_dictionary_keysym_t pdk;
716 prop_object_t po;
717 unsigned int idx;
719 if (! prop_object_is_dictionary(opd))
720 return (NULL);
722 _PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
724 pd = _prop_dictionary_alloc(opd->pd_count);
725 if (pd != NULL) {
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);
740 return (pd);
744 * prop_dictionary_copy_mutable --
745 * Like prop_dictionary_copy(), but the resulting dictionary is
746 * mutable.
748 prop_dictionary_t
749 prop_dictionary_copy_mutable(prop_dictionary_t opd)
751 prop_dictionary_t pd;
753 if (! prop_object_is_dictionary(opd))
754 return (NULL);
756 pd = prop_dictionary_copy(opd);
757 if (pd != NULL)
758 pd->pd_flags &= ~PD_F_IMMUTABLE;
760 return (pd);
764 * prop_dictionary_make_immutable --
765 * Set the immutable flag on that dictionary.
767 void
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.
781 unsigned int
782 prop_dictionary_count(prop_dictionary_t pd)
784 unsigned int rv;
786 if (! prop_object_is_dictionary(pd))
787 return (0);
789 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
790 rv = pd->pd_count;
791 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
793 return (rv);
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
800 * the dictionary).
802 bool
803 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
805 bool rv;
807 if (! prop_object_is_dictionary(pd))
808 return (false);
810 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
811 if (capacity > pd->pd_capacity)
812 rv = _prop_dictionary_expand(pd, capacity);
813 else
814 rv = true;
815 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
816 return (rv);
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))
825 return (NULL);
827 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
828 if (pdi == NULL)
829 return (NULL);
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
842 * the iterator.
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);
852 return (pi);
856 * prop_dictionary_all_keys --
857 * Return an array containing a snapshot of all of the keys
858 * in the dictionary.
860 prop_array_t
861 prop_dictionary_all_keys(prop_dictionary_t pd)
863 prop_array_t array;
864 unsigned int idx;
865 bool rv = true;
867 if (! prop_object_is_dictionary(pd))
868 return (NULL);
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);
877 if (rv == false)
878 break;
881 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
883 if (rv == false) {
884 prop_object_release(array);
885 array = NULL;
887 return (array);
890 static struct _prop_dict_entry *
891 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
892 unsigned int *idxp)
894 struct _prop_dict_entry *pde;
895 unsigned int base, idx, distance;
896 int res;
899 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
902 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
903 distance >>= 1) {
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);
908 if (res == 0) {
909 if (idxp != NULL)
910 *idxp = idx;
911 return (pde);
913 if (res > 0) { /* key > pdk_key: move right */
914 base = idx + 1;
915 distance--;
916 } /* else move left */
919 /* idx points to the slot we looked at last. */
920 if (idxp != NULL)
921 *idxp = idx;
922 return (NULL);
925 static prop_object_t
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))
932 return (NULL);
934 if (!locked)
935 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
936 pde = _prop_dict_lookup(pd, key, NULL);
937 if (pde != NULL) {
938 _PROP_ASSERT(pde->pde_objref != NULL);
939 po = pde->pde_objref;
941 if (!locked)
942 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
943 return (po);
946 * prop_dictionary_get --
947 * Return the object stored with specified key.
949 prop_object_t
950 prop_dictionary_get(prop_dictionary_t pd, const char *key)
952 prop_object_t po;
954 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
955 po = _prop_dictionary_get(pd, key, true);
956 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
957 return (po);
960 static prop_object_t
961 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
962 bool locked)
965 if (! (prop_object_is_dictionary(pd) &&
966 prop_object_is_dictionary_keysym(pdk)))
967 return (NULL);
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.
976 prop_object_t
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.
988 bool
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;
993 unsigned int idx;
994 bool rv = false;
996 if (! prop_object_is_dictionary(pd))
997 return (false);
999 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
1001 if (prop_dictionary_is_immutable(pd))
1002 return (false);
1004 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1006 pde = _prop_dict_lookup(pd, key, &idx);
1007 if (pde != NULL) {
1008 prop_object_t opo = pde->pde_objref;
1009 prop_object_retain(po);
1010 pde->pde_objref = po;
1011 prop_object_release(opo);
1012 rv = true;
1013 goto out;
1016 pdk = _prop_dict_keysym_alloc(key);
1017 if (pdk == NULL)
1018 goto out;
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);
1024 goto out;
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;
1033 pd->pd_count++;
1034 pd->pd_version++;
1035 rv = true;
1036 goto out;
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
1046 * index first.
1048 * Because we're unsigned, we have to special case 0
1049 * (grumble).
1051 if (idx == 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;
1056 pd->pd_count++;
1057 pd->pd_version++;
1058 rv = true;
1059 goto out;
1061 idx--;
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;
1068 pd->pd_count++;
1070 pd->pd_version++;
1072 rv = true;
1074 out:
1075 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1076 return (rv);
1080 * prop_dictionary_set_keysym --
1081 * Replace the object in the dictionary at the location encoded by
1082 * the keysym.
1084 bool
1085 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
1086 prop_object_t po)
1089 if (! (prop_object_is_dictionary(pd) &&
1090 prop_object_is_dictionary_keysym(pdk)))
1091 return (false);
1093 return (prop_dictionary_set(pd, pdk->pdk_key, po));
1096 static void
1097 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
1098 unsigned int idx)
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]);
1111 idx++;
1112 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
1113 (pd->pd_count - idx) * sizeof(*pde));
1114 pd->pd_count--;
1115 pd->pd_version++;
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
1126 * the dictionary.
1128 void
1129 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
1131 struct _prop_dict_entry *pde;
1132 unsigned int idx;
1134 if (! prop_object_is_dictionary(pd))
1135 return;
1137 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1139 /* XXX Should this be a _PROP_ASSERT()? */
1140 if (prop_dictionary_is_immutable(pd))
1141 goto out;
1143 pde = _prop_dict_lookup(pd, key, &idx);
1144 /* XXX Should this be a _PROP_ASSERT()? */
1145 if (pde == NULL)
1146 goto out;
1148 _prop_dictionary_remove(pd, pde, idx);
1149 out:
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.
1158 void
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)))
1165 return;
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.
1175 bool
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))
1180 return (false);
1182 return (prop_object_equals(dict1, dict2));
1186 * prop_dictionary_keysym_cstring_nocopy --
1187 * Return an immutable reference to the keysym's value.
1189 const char *
1190 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
1193 if (! prop_object_is_dictionary_keysym(pdk))
1194 return (NULL);
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.
1204 bool
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))
1210 return (false);
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.
1221 char *
1222 prop_dictionary_externalize(prop_dictionary_t pd)
1224 struct _prop_object_externalize_context *ctx;
1225 char *cp;
1227 ctx = _prop_object_externalize_context_alloc();
1228 if (ctx == NULL)
1229 return (NULL);
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);
1237 return (NULL);
1240 cp = ctx->poec_buf;
1241 _prop_object_externalize_context_free(ctx);
1243 return (cp);
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
1252 * key.
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 *);
1262 bool
1263 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
1264 struct _prop_object_internalize_context *ctx)
1266 prop_dictionary_t dict;
1267 char *tmpkey;
1269 /* We don't currently understand any attributes. */
1270 if (ctx->poic_tagattr != NULL)
1271 return (true);
1273 dict = prop_dictionary_create();
1274 if (dict == NULL)
1275 return (true);
1277 if (ctx->poic_is_empty_element) {
1278 *obj = dict;
1279 return (true);
1282 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1283 if (tmpkey == NULL) {
1284 prop_object_release(dict);
1285 return (true);
1288 *obj = 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);
1296 static bool
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);
1308 if (child != NULL)
1309 prop_object_release(child);
1310 prop_object_release(dict);
1311 *obj = NULL;
1312 return (true);
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);
1324 static bool
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;
1329 size_t keylen;
1331 /* Fetch the next tag. */
1332 if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
1333 goto bad;
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);
1339 return (true);
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)
1346 goto bad;
1348 if (_prop_object_internalize_decode_string(ctx,
1349 tmpkey, PDK_MAXKEY, &keylen,
1350 &ctx->poic_cp) == false)
1351 goto bad;
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)
1358 goto bad;
1360 /* ..and now the beginning of the value. */
1361 if (_prop_object_internalize_find_tag(ctx, NULL,
1362 _PROP_TAG_TYPE_START) == false)
1363 goto bad;
1366 * Key is found, now wait for value to be parsed.
1368 if (_prop_stack_push(stack, *obj,
1369 _prop_dictionary_internalize_continue,
1370 tmpkey, NULL))
1371 return (false);
1373 bad:
1374 _PROP_FREE(tmpkey, M_TEMP);
1375 prop_object_release(dict);
1376 *obj = NULL;
1377 return (true);
1381 * prop_dictionary_internalize --
1382 * Create a dictionary by parsing the NUL-terminated XML-style
1383 * representation.
1385 prop_dictionary_t
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.
1395 bool
1396 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1398 char *xml;
1399 bool rv;
1400 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */
1402 xml = prop_dictionary_externalize(dict);
1403 if (xml == NULL)
1404 return (false);
1405 rv = _prop_object_externalize_write_file(fname, xml,
1406 strlen(xml), false);
1407 if (rv == false)
1408 save_errno = errno;
1409 _PROP_FREE(xml, M_TEMP);
1410 if (rv == false)
1411 errno = save_errno;
1413 return (rv);
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).
1421 bool
1422 prop_dictionary_externalize_to_zfile(prop_dictionary_t dict, const char *fname)
1424 char *xml;
1425 bool rv;
1426 int save_errno = 0;
1428 xml = prop_dictionary_externalize(dict);
1429 if (xml == NULL)
1430 return false;
1431 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml), true);
1432 if (rv == false)
1433 save_errno = errno;
1434 _PROP_FREE(xml, M_TEMP);
1435 if (rv == false)
1436 errno = save_errno;
1438 return rv;
1442 * prop_dictionary_internalize_from_file --
1443 * Internalize a dictionary from a file.
1445 prop_dictionary_t
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);
1452 if (mf == NULL)
1453 return (NULL);
1454 dict = prop_dictionary_internalize(mf->poimf_xml);
1455 _prop_object_internalize_unmap_file(mf);
1457 return (dict);
1460 #define _READ_CHUNK 512
1462 * prop_dictionary_internalize_from_zfile ---
1463 * Internalize a dictionary from a compressed gzip file.
1465 prop_dictionary_t
1466 prop_dictionary_internalize_from_zfile(const char *fname)
1468 struct _prop_object_internalize_mapped_file *mf;
1469 prop_dictionary_t dict;
1470 z_stream strm;
1471 unsigned char out[_READ_CHUNK];
1472 char *uncomp_xml = NULL;
1473 size_t have;
1474 ssize_t totalsize = 0;
1475 int rv = 0;
1477 mf = _prop_object_internalize_map_file(fname);
1478 if (mf == NULL)
1479 return NULL;
1481 /* Decompress the mmap'ed buffer with zlib */
1482 strm.zalloc = Z_NULL;
1483 strm.zfree = Z_NULL;
1484 strm.opaque = Z_NULL;
1485 strm.avail_in = 0;
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);
1491 return NULL;
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);
1501 return NULL;
1504 /* Inflate the input buffer and copy into 'uncomp_xml' */
1505 do {
1506 strm.avail_out = _READ_CHUNK;
1507 strm.next_out = out;
1508 rv = inflate(&strm, Z_NO_FLUSH);
1509 switch (rv) {
1510 case Z_DATA_ERROR:
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);
1519 return dict;
1520 case Z_STREAM_ERROR:
1521 case Z_NEED_DICT:
1522 case Z_MEM_ERROR:
1523 (void)inflateEnd(&strm);
1524 _PROP_FREE(uncomp_xml, M_TEMP);
1525 _prop_object_internalize_unmap_file(mf);
1526 errno = rv;
1527 return NULL;
1529 have = _READ_CHUNK - strm.avail_out;
1530 totalsize += have;
1531 uncomp_xml = _PROP_REALLOC(uncomp_xml, totalsize, M_TEMP);
1532 memcpy(uncomp_xml + totalsize - have, out, have);
1533 } while (strm.avail_out == 0);
1535 /* we are done */
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);
1541 return dict;