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