1 1.39 thorpej /* $NetBSD: prop_number.c,v 1.39 2025/05/14 03:25:46 thorpej Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /*- 4 1.35 thorpej * Copyright (c) 2006, 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.31 thorpej #include "prop_object_impl.h" 33 1.31 thorpej #include <prop/prop_number.h> 34 1.28 pgoyette #include <sys/rbtree.h> 35 1.1 thorpej 36 1.1 thorpej #if defined(_KERNEL) 37 1.1 thorpej #include <sys/systm.h> 38 1.1 thorpej #elif defined(_STANDALONE) 39 1.1 thorpej #include <sys/param.h> 40 1.1 thorpej #include <lib/libkern/libkern.h> 41 1.1 thorpej #else 42 1.1 thorpej #include <errno.h> 43 1.32 thorpej #include <limits.h> 44 1.1 thorpej #include <stdlib.h> 45 1.1 thorpej #endif 46 1.1 thorpej 47 1.27 matt struct _prop_number_value { 48 1.27 matt union { 49 1.27 matt int64_t pnu_signed; 50 1.27 matt uint64_t pnu_unsigned; 51 1.27 matt } pnv_un; 52 1.27 matt #define pnv_signed pnv_un.pnu_signed 53 1.27 matt #define pnv_unsigned pnv_un.pnu_unsigned 54 1.27 matt unsigned int pnv_is_unsigned :1, 55 1.27 matt :31; 56 1.27 matt }; 57 1.27 matt 58 1.1 thorpej struct _prop_number { 59 1.1 thorpej struct _prop_object pn_obj; 60 1.6 thorpej struct rb_node pn_link; 61 1.27 matt struct _prop_number_value pn_value; 62 1.1 thorpej }; 63 1.1 thorpej 64 1.1 thorpej _PROP_POOL_INIT(_prop_number_pool, sizeof(struct _prop_number), "propnmbr") 65 1.1 thorpej 66 1.35 thorpej static const struct _prop_object_type_tags _prop_number_type_tags = { 67 1.35 thorpej .xml_tag = "integer", 68 1.35 thorpej }; 69 1.35 thorpej 70 1.19 thorpej static _prop_object_free_rv_t 71 1.19 thorpej _prop_number_free(prop_stack_t, prop_object_t *); 72 1.12 thorpej static bool _prop_number_externalize( 73 1.2 thorpej struct _prop_object_externalize_context *, 74 1.2 thorpej void *); 75 1.19 thorpej static _prop_object_equals_rv_t 76 1.19 thorpej _prop_number_equals(prop_object_t, prop_object_t, 77 1.14 joerg void **, void **, 78 1.14 joerg prop_object_t *, prop_object_t *); 79 1.2 thorpej 80 1.20 haad static void _prop_number_lock(void); 81 1.20 haad static void _prop_number_unlock(void); 82 1.20 haad 83 1.2 thorpej static const struct _prop_object_type _prop_object_type_number = { 84 1.2 thorpej .pot_type = PROP_TYPE_NUMBER, 85 1.2 thorpej .pot_free = _prop_number_free, 86 1.2 thorpej .pot_extern = _prop_number_externalize, 87 1.2 thorpej .pot_equals = _prop_number_equals, 88 1.20 haad .pot_lock = _prop_number_lock, 89 1.20 haad .pot_unlock = _prop_number_unlock, 90 1.2 thorpej }; 91 1.2 thorpej 92 1.2 thorpej #define prop_object_is_number(x) \ 93 1.5 thorpej ((x) != NULL && (x)->pn_obj.po_type == &_prop_object_type_number) 94 1.1 thorpej 95 1.6 thorpej /* 96 1.6 thorpej * Number objects are immutable, and we are likely to have many number 97 1.6 thorpej * objects that have the same value. So, to save memory, we unique'ify 98 1.6 thorpej * numbers so we only have one copy of each. 99 1.6 thorpej */ 100 1.6 thorpej 101 1.6 thorpej static int 102 1.8 thorpej _prop_number_compare_values(const struct _prop_number_value *pnv1, 103 1.8 thorpej const struct _prop_number_value *pnv2) 104 1.8 thorpej { 105 1.8 thorpej 106 1.8 thorpej /* Signed numbers are sorted before unsigned numbers. */ 107 1.8 thorpej 108 1.8 thorpej if (pnv1->pnv_is_unsigned) { 109 1.8 thorpej if (! pnv2->pnv_is_unsigned) 110 1.8 thorpej return (1); 111 1.8 thorpej if (pnv1->pnv_unsigned < pnv2->pnv_unsigned) 112 1.8 thorpej return (-1); 113 1.8 thorpej if (pnv1->pnv_unsigned > pnv2->pnv_unsigned) 114 1.8 thorpej return (1); 115 1.8 thorpej return (0); 116 1.8 thorpej } 117 1.8 thorpej 118 1.8 thorpej if (pnv2->pnv_is_unsigned) 119 1.8 thorpej return (-1); 120 1.8 thorpej if (pnv1->pnv_signed < pnv2->pnv_signed) 121 1.8 thorpej return (-1); 122 1.8 thorpej if (pnv1->pnv_signed > pnv2->pnv_signed) 123 1.8 thorpej return (1); 124 1.8 thorpej return (0); 125 1.8 thorpej } 126 1.8 thorpej 127 1.8 thorpej static int 128 1.23 rmind /*ARGSUSED*/ 129 1.24 pooka _prop_number_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED, 130 1.23 rmind const void *n1, const void *n2) 131 1.6 thorpej { 132 1.23 rmind const struct _prop_number *pn1 = n1; 133 1.23 rmind const struct _prop_number *pn2 = n2; 134 1.6 thorpej 135 1.23 rmind return _prop_number_compare_values(&pn1->pn_value, &pn2->pn_value); 136 1.6 thorpej } 137 1.6 thorpej 138 1.6 thorpej static int 139 1.23 rmind /*ARGSUSED*/ 140 1.24 pooka _prop_number_rb_compare_key(void *ctx _PROP_ARG_UNUSED, 141 1.24 pooka const void *n, const void *v) 142 1.6 thorpej { 143 1.23 rmind const struct _prop_number *pn = n; 144 1.8 thorpej const struct _prop_number_value *pnv = v; 145 1.6 thorpej 146 1.23 rmind return _prop_number_compare_values(&pn->pn_value, pnv); 147 1.6 thorpej } 148 1.6 thorpej 149 1.23 rmind static const rb_tree_ops_t _prop_number_rb_tree_ops = { 150 1.6 thorpej .rbto_compare_nodes = _prop_number_rb_compare_nodes, 151 1.23 rmind .rbto_compare_key = _prop_number_rb_compare_key, 152 1.23 rmind .rbto_node_offset = offsetof(struct _prop_number, pn_link), 153 1.23 rmind .rbto_context = NULL 154 1.6 thorpej }; 155 1.6 thorpej 156 1.6 thorpej static struct rb_tree _prop_number_tree; 157 1.7 thorpej _PROP_MUTEX_DECL_STATIC(_prop_number_tree_mutex) 158 1.6 thorpej 159 1.13 joerg /* ARGSUSED */ 160 1.19 thorpej static _prop_object_free_rv_t 161 1.13 joerg _prop_number_free(prop_stack_t stack, prop_object_t *obj) 162 1.1 thorpej { 163 1.13 joerg prop_number_t pn = *obj; 164 1.6 thorpej 165 1.28 pgoyette rb_tree_remove_node(&_prop_number_tree, pn); 166 1.1 thorpej 167 1.6 thorpej _PROP_POOL_PUT(_prop_number_pool, pn); 168 1.13 joerg 169 1.13 joerg return (_PROP_OBJECT_FREE_DONE); 170 1.1 thorpej } 171 1.1 thorpej 172 1.21 pooka _PROP_ONCE_DECL(_prop_number_init_once) 173 1.21 pooka 174 1.21 pooka static int 175 1.21 pooka _prop_number_init(void) 176 1.21 pooka { 177 1.21 pooka 178 1.21 pooka _PROP_MUTEX_INIT(_prop_number_tree_mutex); 179 1.28 pgoyette rb_tree_init(&_prop_number_tree, &_prop_number_rb_tree_ops); 180 1.21 pooka return 0; 181 1.21 pooka } 182 1.21 pooka 183 1.34 riastrad static void 184 1.22 cegger _prop_number_lock(void) 185 1.20 haad { 186 1.21 pooka /* XXX: init necessary? */ 187 1.21 pooka _PROP_ONCE_RUN(_prop_number_init_once, _prop_number_init); 188 1.20 haad _PROP_MUTEX_LOCK(_prop_number_tree_mutex); 189 1.20 haad } 190 1.20 haad 191 1.20 haad static void 192 1.22 cegger _prop_number_unlock(void) 193 1.20 haad { 194 1.20 haad _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); 195 1.20 haad } 196 1.34 riastrad 197 1.12 thorpej static bool 198 1.1 thorpej _prop_number_externalize(struct _prop_object_externalize_context *ctx, 199 1.1 thorpej void *v) 200 1.1 thorpej { 201 1.1 thorpej prop_number_t pn = v; 202 1.1 thorpej char tmpstr[32]; 203 1.35 thorpej const bool json = ctx->poec_format == PROP_FORMAT_JSON; 204 1.35 thorpej 205 1.35 thorpej _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || 206 1.35 thorpej ctx->poec_format == PROP_FORMAT_JSON); 207 1.1 thorpej 208 1.8 thorpej /* 209 1.35 thorpej * For unsigned numbers, we output in hex for XML, decimal for JSON. 210 1.35 thorpej * For signed numbers, we output in decimal for both. 211 1.8 thorpej */ 212 1.35 thorpej if (pn->pn_value.pnv_is_unsigned) { 213 1.35 thorpej snprintf(tmpstr, sizeof(tmpstr), 214 1.35 thorpej json ? "%" PRIu64 : "0x%" PRIx64, 215 1.26 christos pn->pn_value.pnv_unsigned); 216 1.35 thorpej } else { 217 1.26 christos snprintf(tmpstr, sizeof(tmpstr), "%" PRIi64, 218 1.26 christos pn->pn_value.pnv_signed); 219 1.35 thorpej } 220 1.1 thorpej 221 1.39 thorpej if (_prop_extern_append_start_tag(ctx, 222 1.35 thorpej &_prop_number_type_tags, NULL) == false || 223 1.39 thorpej _prop_extern_append_cstring(ctx, tmpstr) == false || 224 1.39 thorpej _prop_extern_append_end_tag(ctx, 225 1.35 thorpej &_prop_number_type_tags) == false) { 226 1.35 thorpej return false; 227 1.35 thorpej } 228 1.34 riastrad 229 1.35 thorpej return true; 230 1.1 thorpej } 231 1.1 thorpej 232 1.14 joerg /* ARGSUSED */ 233 1.19 thorpej static _prop_object_equals_rv_t 234 1.14 joerg _prop_number_equals(prop_object_t v1, prop_object_t v2, 235 1.14 joerg void **stored_pointer1, void **stored_pointer2, 236 1.14 joerg prop_object_t *next_obj1, prop_object_t *next_obj2) 237 1.2 thorpej { 238 1.2 thorpej prop_number_t num1 = v1; 239 1.2 thorpej prop_number_t num2 = v2; 240 1.2 thorpej 241 1.6 thorpej /* 242 1.6 thorpej * There is only ever one copy of a number object at any given 243 1.8 thorpej * time, so we can reduce this to a simple pointer equality check 244 1.8 thorpej * in the common case. 245 1.8 thorpej */ 246 1.8 thorpej if (num1 == num2) 247 1.14 joerg return (_PROP_OBJECT_EQUALS_TRUE); 248 1.8 thorpej 249 1.8 thorpej /* 250 1.8 thorpej * If the numbers are the same signed-ness, then we know they 251 1.8 thorpej * cannot be equal because they would have had pointer equality. 252 1.6 thorpej */ 253 1.8 thorpej if (num1->pn_value.pnv_is_unsigned == num2->pn_value.pnv_is_unsigned) 254 1.19 thorpej return (_PROP_OBJECT_EQUALS_FALSE); 255 1.8 thorpej 256 1.8 thorpej /* 257 1.8 thorpej * We now have one signed value and one unsigned value. We can 258 1.8 thorpej * compare them iff: 259 1.8 thorpej * - The unsigned value is not larger than the signed value 260 1.8 thorpej * can represent. 261 1.8 thorpej * - The signed value is not smaller than the unsigned value 262 1.8 thorpej * can represent. 263 1.8 thorpej */ 264 1.8 thorpej if (num1->pn_value.pnv_is_unsigned) { 265 1.8 thorpej /* 266 1.8 thorpej * num1 is unsigned and num2 is signed. 267 1.8 thorpej */ 268 1.32 thorpej if (num1->pn_value.pnv_unsigned > INTMAX_MAX) 269 1.14 joerg return (_PROP_OBJECT_EQUALS_FALSE); 270 1.8 thorpej if (num2->pn_value.pnv_signed < 0) 271 1.14 joerg return (_PROP_OBJECT_EQUALS_FALSE); 272 1.8 thorpej } else { 273 1.8 thorpej /* 274 1.8 thorpej * num1 is signed and num2 is unsigned. 275 1.8 thorpej */ 276 1.8 thorpej if (num1->pn_value.pnv_signed < 0) 277 1.14 joerg return (_PROP_OBJECT_EQUALS_FALSE); 278 1.32 thorpej if (num2->pn_value.pnv_unsigned > INTMAX_MAX) 279 1.14 joerg return (_PROP_OBJECT_EQUALS_FALSE); 280 1.8 thorpej } 281 1.8 thorpej 282 1.14 joerg if (num1->pn_value.pnv_signed == num2->pn_value.pnv_signed) 283 1.14 joerg return _PROP_OBJECT_EQUALS_TRUE; 284 1.14 joerg else 285 1.14 joerg return _PROP_OBJECT_EQUALS_FALSE; 286 1.2 thorpej } 287 1.2 thorpej 288 1.1 thorpej static prop_number_t 289 1.8 thorpej _prop_number_alloc(const struct _prop_number_value *pnv) 290 1.1 thorpej { 291 1.23 rmind prop_number_t opn, pn, rpn; 292 1.6 thorpej 293 1.21 pooka _PROP_ONCE_RUN(_prop_number_init_once, _prop_number_init); 294 1.21 pooka 295 1.6 thorpej /* 296 1.6 thorpej * Check to see if this already exists in the tree. If it does, 297 1.6 thorpej * we just retain it and return it. 298 1.6 thorpej */ 299 1.6 thorpej _PROP_MUTEX_LOCK(_prop_number_tree_mutex); 300 1.29 pgoyette opn = rb_tree_find_node(&_prop_number_tree, pnv); 301 1.23 rmind if (opn != NULL) { 302 1.21 pooka prop_object_retain(opn); 303 1.21 pooka _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); 304 1.21 pooka return (opn); 305 1.6 thorpej } 306 1.6 thorpej _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); 307 1.6 thorpej 308 1.6 thorpej /* 309 1.6 thorpej * Not in the tree. Create it now. 310 1.6 thorpej */ 311 1.1 thorpej 312 1.1 thorpej pn = _PROP_POOL_GET(_prop_number_pool); 313 1.6 thorpej if (pn == NULL) 314 1.6 thorpej return (NULL); 315 1.6 thorpej 316 1.6 thorpej _prop_object_init(&pn->pn_obj, &_prop_object_type_number); 317 1.1 thorpej 318 1.8 thorpej pn->pn_value = *pnv; 319 1.6 thorpej 320 1.6 thorpej /* 321 1.6 thorpej * We dropped the mutex when we allocated the new object, so 322 1.6 thorpej * we have to check again if it is in the tree. 323 1.6 thorpej */ 324 1.6 thorpej _PROP_MUTEX_LOCK(_prop_number_tree_mutex); 325 1.30 pgoyette opn = rb_tree_find_node(&_prop_number_tree, pnv); 326 1.23 rmind if (opn != NULL) { 327 1.6 thorpej prop_object_retain(opn); 328 1.6 thorpej _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); 329 1.6 thorpej _PROP_POOL_PUT(_prop_number_pool, pn); 330 1.6 thorpej return (opn); 331 1.1 thorpej } 332 1.28 pgoyette rpn = rb_tree_insert_node(&_prop_number_tree, pn); 333 1.23 rmind _PROP_ASSERT(rpn == pn); 334 1.6 thorpej _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); 335 1.25 martin return (rpn); 336 1.1 thorpej } 337 1.1 thorpej 338 1.1 thorpej /* 339 1.32 thorpej * prop_number_create_signed -- 340 1.1 thorpej * Create a prop_number_t and initialize it with the 341 1.32 thorpej * provided signed value. 342 1.1 thorpej */ 343 1.36 thorpej _PROP_EXPORT prop_number_t 344 1.32 thorpej prop_number_create_signed(intmax_t val) 345 1.1 thorpej { 346 1.9 thorpej struct _prop_number_value pnv; 347 1.9 thorpej 348 1.9 thorpej memset(&pnv, 0, sizeof(pnv)); 349 1.9 thorpej pnv.pnv_signed = val; 350 1.12 thorpej pnv.pnv_is_unsigned = false; 351 1.1 thorpej 352 1.8 thorpej return (_prop_number_alloc(&pnv)); 353 1.8 thorpej } 354 1.8 thorpej 355 1.32 thorpej _PROP_DEPRECATED(prop_number_create_integer, 356 1.32 thorpej "this program uses prop_number_create_integer(), " 357 1.32 thorpej "which is deprecated; use prop_number_create_signed() instead.") 358 1.36 thorpej _PROP_EXPORT prop_number_t 359 1.32 thorpej prop_number_create_integer(int64_t val) 360 1.32 thorpej { 361 1.32 thorpej return prop_number_create_signed(val); 362 1.32 thorpej } 363 1.32 thorpej 364 1.8 thorpej /* 365 1.32 thorpej * prop_number_create_unsigned -- 366 1.8 thorpej * Create a prop_number_t and initialize it with the 367 1.32 thorpej * provided unsigned value. 368 1.8 thorpej */ 369 1.36 thorpej _PROP_EXPORT prop_number_t 370 1.32 thorpej prop_number_create_unsigned(uintmax_t val) 371 1.8 thorpej { 372 1.9 thorpej struct _prop_number_value pnv; 373 1.9 thorpej 374 1.9 thorpej memset(&pnv, 0, sizeof(pnv)); 375 1.9 thorpej pnv.pnv_unsigned = val; 376 1.12 thorpej pnv.pnv_is_unsigned = true; 377 1.8 thorpej 378 1.8 thorpej return (_prop_number_alloc(&pnv)); 379 1.1 thorpej } 380 1.1 thorpej 381 1.32 thorpej _PROP_DEPRECATED(prop_number_create_unsigned_integer, 382 1.33 thorpej "this program uses prop_number_create_unsigned_integer(), " 383 1.32 thorpej "which is deprecated; use prop_number_create_unsigned() instead.") 384 1.36 thorpej _PROP_EXPORT prop_number_t 385 1.32 thorpej prop_number_create_unsigned_integer(uint64_t val) 386 1.32 thorpej { 387 1.32 thorpej return prop_number_create_unsigned(val); 388 1.32 thorpej } 389 1.32 thorpej 390 1.1 thorpej /* 391 1.1 thorpej * prop_number_copy -- 392 1.1 thorpej * Copy a prop_number_t. 393 1.1 thorpej */ 394 1.36 thorpej _PROP_EXPORT prop_number_t 395 1.1 thorpej prop_number_copy(prop_number_t opn) 396 1.1 thorpej { 397 1.1 thorpej 398 1.4 thorpej if (! prop_object_is_number(opn)) 399 1.4 thorpej return (NULL); 400 1.1 thorpej 401 1.6 thorpej /* 402 1.6 thorpej * Because we only ever allocate one object for any given 403 1.6 thorpej * value, this can be reduced to a simple retain operation. 404 1.6 thorpej */ 405 1.6 thorpej prop_object_retain(opn); 406 1.6 thorpej return (opn); 407 1.1 thorpej } 408 1.1 thorpej 409 1.1 thorpej /* 410 1.8 thorpej * prop_number_unsigned -- 411 1.12 thorpej * Returns true if the prop_number_t has an unsigned value. 412 1.8 thorpej */ 413 1.36 thorpej _PROP_EXPORT bool 414 1.8 thorpej prop_number_unsigned(prop_number_t pn) 415 1.8 thorpej { 416 1.8 thorpej 417 1.8 thorpej return (pn->pn_value.pnv_is_unsigned); 418 1.8 thorpej } 419 1.8 thorpej 420 1.8 thorpej /* 421 1.1 thorpej * prop_number_size -- 422 1.1 thorpej * Return the size, in bits, required to hold the value of 423 1.1 thorpej * the specified number. 424 1.1 thorpej */ 425 1.36 thorpej _PROP_EXPORT int 426 1.1 thorpej prop_number_size(prop_number_t pn) 427 1.1 thorpej { 428 1.8 thorpej struct _prop_number_value *pnv; 429 1.1 thorpej 430 1.4 thorpej if (! prop_object_is_number(pn)) 431 1.4 thorpej return (0); 432 1.4 thorpej 433 1.8 thorpej pnv = &pn->pn_value; 434 1.8 thorpej 435 1.8 thorpej if (pnv->pnv_is_unsigned) { 436 1.8 thorpej if (pnv->pnv_unsigned > UINT32_MAX) 437 1.8 thorpej return (64); 438 1.8 thorpej if (pnv->pnv_unsigned > UINT16_MAX) 439 1.8 thorpej return (32); 440 1.8 thorpej if (pnv->pnv_unsigned > UINT8_MAX) 441 1.8 thorpej return (16); 442 1.8 thorpej return (8); 443 1.8 thorpej } 444 1.8 thorpej 445 1.8 thorpej if (pnv->pnv_signed > INT32_MAX || pnv->pnv_signed < INT32_MIN) 446 1.8 thorpej return (64); 447 1.8 thorpej if (pnv->pnv_signed > INT16_MAX || pnv->pnv_signed < INT16_MIN) 448 1.1 thorpej return (32); 449 1.8 thorpej if (pnv->pnv_signed > INT8_MAX || pnv->pnv_signed < INT8_MIN) 450 1.1 thorpej return (16); 451 1.1 thorpej return (8); 452 1.1 thorpej } 453 1.1 thorpej 454 1.1 thorpej /* 455 1.32 thorpej * prop_number_signed_value -- 456 1.32 thorpej * Get the signed value of a prop_number_t. 457 1.1 thorpej */ 458 1.36 thorpej _PROP_EXPORT intmax_t 459 1.32 thorpej prop_number_signed_value(prop_number_t pn) 460 1.8 thorpej { 461 1.8 thorpej 462 1.8 thorpej /* 463 1.8 thorpej * XXX Impossible to distinguish between "not a prop_number_t" 464 1.8 thorpej * XXX and "prop_number_t has a value of 0". 465 1.8 thorpej */ 466 1.8 thorpej if (! prop_object_is_number(pn)) 467 1.8 thorpej return (0); 468 1.8 thorpej 469 1.8 thorpej return (pn->pn_value.pnv_signed); 470 1.8 thorpej } 471 1.8 thorpej 472 1.32 thorpej _PROP_DEPRECATED(prop_number_integer_value, 473 1.32 thorpej "this program uses prop_number_integer_value(), " 474 1.32 thorpej "which is deprecated; use prop_number_signed_value() instead.") 475 1.36 thorpej _PROP_EXPORT int64_t 476 1.32 thorpej prop_number_integer_value(prop_number_t pn) 477 1.32 thorpej { 478 1.32 thorpej return prop_number_signed_value(pn); 479 1.32 thorpej } 480 1.32 thorpej 481 1.8 thorpej /* 482 1.32 thorpej * prop_number_unsigned_value -- 483 1.32 thorpej * Get the unsigned value of a prop_number_t. 484 1.8 thorpej */ 485 1.36 thorpej _PROP_EXPORT uintmax_t 486 1.32 thorpej prop_number_unsigned_value(prop_number_t pn) 487 1.1 thorpej { 488 1.1 thorpej 489 1.4 thorpej /* 490 1.4 thorpej * XXX Impossible to distinguish between "not a prop_number_t" 491 1.4 thorpej * XXX and "prop_number_t has a value of 0". 492 1.4 thorpej */ 493 1.4 thorpej if (! prop_object_is_number(pn)) 494 1.4 thorpej return (0); 495 1.4 thorpej 496 1.8 thorpej return (pn->pn_value.pnv_unsigned); 497 1.1 thorpej } 498 1.1 thorpej 499 1.32 thorpej _PROP_DEPRECATED(prop_number_unsigned_integer_value, 500 1.32 thorpej "this program uses prop_number_unsigned_integer_value(), " 501 1.32 thorpej "which is deprecated; use prop_number_unsigned_value() instead.") 502 1.36 thorpej _PROP_EXPORT uint64_t 503 1.32 thorpej prop_number_unsigned_integer_value(prop_number_t pn) 504 1.32 thorpej { 505 1.32 thorpej return prop_number_unsigned_value(pn); 506 1.32 thorpej } 507 1.32 thorpej 508 1.32 thorpej /* 509 1.32 thorpej * prop_number_[...]_value -- 510 1.32 thorpej * Retrieve the bounds-checked value as the specified type. 511 1.32 thorpej * Returns true if successful. 512 1.32 thorpej */ 513 1.32 thorpej #define TEMPLATE(name, typ, minv, maxv) \ 514 1.36 thorpej _PROP_EXPORT bool \ 515 1.32 thorpej prop_number_ ## name ## _value(prop_number_t pn, typ * const valp) \ 516 1.32 thorpej { \ 517 1.32 thorpej \ 518 1.32 thorpej if (! prop_object_is_number(pn)) \ 519 1.32 thorpej return (false); \ 520 1.32 thorpej \ 521 1.32 thorpej if (pn->pn_value.pnv_is_unsigned) { \ 522 1.32 thorpej if (pn->pn_value.pnv_unsigned > (maxv)) \ 523 1.32 thorpej return (false); \ 524 1.32 thorpej *valp = (typ) pn->pn_value.pnv_unsigned; \ 525 1.32 thorpej } else { \ 526 1.32 thorpej if ((pn->pn_value.pnv_signed > 0 && \ 527 1.32 thorpej (uintmax_t)pn->pn_value.pnv_signed > (maxv)) || \ 528 1.32 thorpej pn->pn_value.pnv_signed < (minv)) \ 529 1.32 thorpej return (false); \ 530 1.32 thorpej *valp = (typ) pn->pn_value.pnv_signed; \ 531 1.32 thorpej } \ 532 1.32 thorpej \ 533 1.32 thorpej return (true); \ 534 1.32 thorpej } 535 1.32 thorpej TEMPLATE(schar, signed char, SCHAR_MIN, SCHAR_MAX) 536 1.32 thorpej TEMPLATE(short, short, SHRT_MIN, SHRT_MAX) 537 1.32 thorpej TEMPLATE(int, int, INT_MIN, INT_MAX) 538 1.32 thorpej TEMPLATE(long, long, LONG_MIN, LONG_MAX) 539 1.32 thorpej TEMPLATE(longlong, long long, LLONG_MIN, LLONG_MAX) 540 1.32 thorpej TEMPLATE(intptr, intptr_t, INTPTR_MIN, INTPTR_MAX) 541 1.32 thorpej TEMPLATE(int8, int8_t, INT8_MIN, INT8_MAX) 542 1.32 thorpej TEMPLATE(int16, int16_t, INT16_MIN, INT16_MAX) 543 1.32 thorpej TEMPLATE(int32, int32_t, INT32_MIN, INT32_MAX) 544 1.32 thorpej TEMPLATE(int64, int64_t, INT64_MIN, INT64_MAX) 545 1.32 thorpej 546 1.32 thorpej TEMPLATE(uchar, unsigned char, 0, UCHAR_MAX) 547 1.32 thorpej TEMPLATE(ushort, unsigned short, 0, USHRT_MAX) 548 1.32 thorpej TEMPLATE(uint, unsigned int, 0, UINT_MAX) 549 1.32 thorpej TEMPLATE(ulong, unsigned long, 0, ULONG_MAX) 550 1.32 thorpej TEMPLATE(ulonglong, unsigned long long, 0, ULLONG_MAX) 551 1.32 thorpej TEMPLATE(uintptr, uintptr_t, 0, UINTPTR_MAX) 552 1.32 thorpej TEMPLATE(uint8, uint8_t, 0, UINT8_MAX) 553 1.32 thorpej TEMPLATE(uint16, uint16_t, 0, UINT16_MAX) 554 1.32 thorpej TEMPLATE(uint32, uint32_t, 0, UINT32_MAX) 555 1.32 thorpej TEMPLATE(uint64, uint64_t, 0, UINT64_MAX) 556 1.32 thorpej 557 1.32 thorpej #undef TEMPLATE 558 1.32 thorpej 559 1.1 thorpej /* 560 1.1 thorpej * prop_number_equals -- 561 1.12 thorpej * Return true if two numbers are equivalent. 562 1.1 thorpej */ 563 1.36 thorpej _PROP_EXPORT bool 564 1.1 thorpej prop_number_equals(prop_number_t num1, prop_number_t num2) 565 1.1 thorpej { 566 1.14 joerg if (!prop_object_is_number(num1) || !prop_object_is_number(num2)) 567 1.14 joerg return (false); 568 1.1 thorpej 569 1.14 joerg return (prop_object_equals(num1, num2)); 570 1.1 thorpej } 571 1.1 thorpej 572 1.1 thorpej /* 573 1.32 thorpej * prop_number_equals_signed -- 574 1.32 thorpej * Return true if the number is equivalent to the specified signed 575 1.32 thorpej * value. 576 1.1 thorpej */ 577 1.36 thorpej _PROP_EXPORT bool 578 1.32 thorpej prop_number_equals_signed(prop_number_t pn, intmax_t val) 579 1.1 thorpej { 580 1.1 thorpej 581 1.4 thorpej if (! prop_object_is_number(pn)) 582 1.12 thorpej return (false); 583 1.4 thorpej 584 1.8 thorpej if (pn->pn_value.pnv_is_unsigned && 585 1.32 thorpej (pn->pn_value.pnv_unsigned > INTMAX_MAX || val < 0)) 586 1.12 thorpej return (false); 587 1.34 riastrad 588 1.8 thorpej return (pn->pn_value.pnv_signed == val); 589 1.8 thorpej } 590 1.8 thorpej 591 1.32 thorpej _PROP_DEPRECATED(prop_number_equals_integer, 592 1.32 thorpej "this program uses prop_number_equals_integer(), " 593 1.32 thorpej "which is deprecated; use prop_number_equals_signed() instead.") 594 1.36 thorpej _PROP_EXPORT bool 595 1.32 thorpej prop_number_equals_integer(prop_number_t pn, int64_t val) 596 1.32 thorpej { 597 1.32 thorpej return prop_number_equals_signed(pn, val); 598 1.32 thorpej } 599 1.32 thorpej 600 1.8 thorpej /* 601 1.32 thorpej * prop_number_equals_unsigned -- 602 1.12 thorpej * Return true if the number is equivalent to the specified 603 1.32 thorpej * unsigned value. 604 1.8 thorpej */ 605 1.36 thorpej _PROP_EXPORT bool 606 1.32 thorpej prop_number_equals_unsigned(prop_number_t pn, uintmax_t val) 607 1.8 thorpej { 608 1.8 thorpej 609 1.8 thorpej if (! prop_object_is_number(pn)) 610 1.12 thorpej return (false); 611 1.34 riastrad 612 1.8 thorpej if (! pn->pn_value.pnv_is_unsigned && 613 1.8 thorpej (pn->pn_value.pnv_signed < 0 || val > INT64_MAX)) 614 1.12 thorpej return (false); 615 1.34 riastrad 616 1.8 thorpej return (pn->pn_value.pnv_unsigned == val); 617 1.8 thorpej } 618 1.8 thorpej 619 1.32 thorpej _PROP_DEPRECATED(prop_number_equals_unsigned_integer, 620 1.32 thorpej "this program uses prop_number_equals_unsigned_integer(), " 621 1.32 thorpej "which is deprecated; use prop_number_equals_unsigned() instead.") 622 1.36 thorpej _PROP_EXPORT bool 623 1.32 thorpej prop_number_equals_unsigned_integer(prop_number_t pn, uint64_t val) 624 1.32 thorpej { 625 1.32 thorpej return prop_number_equals_unsigned(pn, val); 626 1.32 thorpej } 627 1.32 thorpej 628 1.12 thorpej static bool 629 1.8 thorpej _prop_number_internalize_unsigned(struct _prop_object_internalize_context *ctx, 630 1.35 thorpej struct _prop_number_value *pnv, int base) 631 1.8 thorpej { 632 1.8 thorpej char *cp; 633 1.8 thorpej 634 1.9 thorpej _PROP_ASSERT(/*CONSTCOND*/sizeof(unsigned long long) == 635 1.9 thorpej sizeof(uint64_t)); 636 1.8 thorpej 637 1.8 thorpej #ifndef _KERNEL 638 1.8 thorpej errno = 0; 639 1.8 thorpej #endif 640 1.35 thorpej pnv->pnv_unsigned = (uint64_t) strtoull(ctx->poic_cp, &cp, base); 641 1.8 thorpej #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ 642 1.8 thorpej if (pnv->pnv_unsigned == UINT64_MAX && errno == ERANGE) 643 1.12 thorpej return (false); 644 1.8 thorpej #endif 645 1.12 thorpej pnv->pnv_is_unsigned = true; 646 1.8 thorpej ctx->poic_cp = cp; 647 1.8 thorpej 648 1.12 thorpej return (true); 649 1.8 thorpej } 650 1.8 thorpej 651 1.12 thorpej static bool 652 1.8 thorpej _prop_number_internalize_signed(struct _prop_object_internalize_context *ctx, 653 1.35 thorpej struct _prop_number_value *pnv, int base) 654 1.8 thorpej { 655 1.8 thorpej char *cp; 656 1.8 thorpej 657 1.9 thorpej _PROP_ASSERT(/*CONSTCOND*/sizeof(long long) == sizeof(int64_t)); 658 1.8 thorpej 659 1.8 thorpej #ifndef _KERNEL 660 1.8 thorpej errno = 0; 661 1.8 thorpej #endif 662 1.35 thorpej pnv->pnv_signed = (int64_t) strtoll(ctx->poic_cp, &cp, base); 663 1.8 thorpej #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ 664 1.8 thorpej if ((pnv->pnv_signed == INT64_MAX || pnv->pnv_signed == INT64_MIN) && 665 1.8 thorpej errno == ERANGE) 666 1.12 thorpej return (false); 667 1.8 thorpej #endif 668 1.12 thorpej pnv->pnv_is_unsigned = false; 669 1.8 thorpej ctx->poic_cp = cp; 670 1.8 thorpej 671 1.12 thorpej return (true); 672 1.1 thorpej } 673 1.1 thorpej 674 1.1 thorpej /* 675 1.1 thorpej * _prop_number_internalize -- 676 1.1 thorpej * Parse a <number>...</number> and return the object created from 677 1.1 thorpej * the external representation. 678 1.1 thorpej */ 679 1.13 joerg /* ARGSUSED */ 680 1.13 joerg bool 681 1.13 joerg _prop_number_internalize(prop_stack_t stack, prop_object_t *obj, 682 1.13 joerg struct _prop_object_internalize_context *ctx) 683 1.1 thorpej { 684 1.9 thorpej struct _prop_number_value pnv; 685 1.9 thorpej 686 1.35 thorpej /* JSON numbers are always base-10. */ 687 1.35 thorpej const int base = ctx->poic_format == PROP_FORMAT_JSON ? 10 : 0; 688 1.35 thorpej 689 1.9 thorpej memset(&pnv, 0, sizeof(pnv)); 690 1.1 thorpej 691 1.1 thorpej /* No attributes, no empty elements. */ 692 1.1 thorpej if (ctx->poic_tagattr != NULL || ctx->poic_is_empty_element) 693 1.13 joerg return (true); 694 1.1 thorpej 695 1.8 thorpej /* 696 1.35 thorpej * If the first character is a '+' or '-', then we treat as signed. 697 1.8 thorpej * If the first two characters are "0x" (i.e. the number is 698 1.8 thorpej * in hex), then we treat as unsigned. Otherwise, we try 699 1.8 thorpej * signed first, and if that fails (presumably due to ERANGE), 700 1.8 thorpej * then we switch to unsigned. 701 1.8 thorpej */ 702 1.35 thorpej if (ctx->poic_cp[0] == '-' || ctx->poic_cp[0] == '+') { 703 1.35 thorpej if (_prop_number_internalize_signed(ctx, &pnv, base) == false) 704 1.13 joerg return (true); 705 1.8 thorpej } else if (ctx->poic_cp[0] == '0' && ctx->poic_cp[1] == 'x') { 706 1.35 thorpej /* No hex numbers in JSON. */ 707 1.35 thorpej if (ctx->poic_format == PROP_FORMAT_JSON || 708 1.35 thorpej _prop_number_internalize_unsigned(ctx, &pnv, 16) == false) 709 1.13 joerg return (true); 710 1.8 thorpej } else { 711 1.35 thorpej if (_prop_number_internalize_signed(ctx, &pnv, base) == false && 712 1.35 thorpej _prop_number_internalize_unsigned(ctx, &pnv, base) == false) 713 1.13 joerg return (true); 714 1.8 thorpej } 715 1.8 thorpej 716 1.35 thorpej /* No end tag to advance over in JSON. */ 717 1.35 thorpej if (ctx->poic_format != PROP_FORMAT_JSON && 718 1.39 thorpej _prop_xml_intern_find_tag(ctx, "integer", 719 1.12 thorpej _PROP_TAG_TYPE_END) == false) 720 1.13 joerg return (true); 721 1.1 thorpej 722 1.13 joerg *obj = _prop_number_alloc(&pnv); 723 1.13 joerg return (true); 724 1.1 thorpej } 725