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