| /* | |
| * Copyright © 2019-2026 Ebrahim Byagowi | |
| * | |
| * This is part of HarfBuzz, a text shaping library. | |
| * | |
| * Permission is hereby granted, without written agreement and without | |
| * license or royalty fees, to use, copy, modify, and distribute this | |
| * software and its documentation for any purpose, provided that the | |
| * above copyright notice and the following two paragraphs appear in | |
| * all copies of this software. | |
| * | |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
| * DAMAGE. | |
| * | |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| * | |
| */ | |
| #ifndef HB_NUMBER_CC | |
| #define HB_NUMBER_CC | |
| #ifdef HB_NUMBER_CC /* Pacify -Wunused-macros. */ | |
| #include "hb.hh" | |
| #include "hb-number.hh" | |
| #include "hb-number-parser.hh" | |
| bool | |
| hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) | |
| { | |
| const char *pend = end; | |
| double value = hb_strtod (*pp, &pend); | |
| if (unlikely (*pp == pend)) return false; | |
| if (unlikely (whole_buffer && end != pend)) return false; | |
| *pv = value; | |
| *pp = pend; | |
| return true; | |
| } | |
| /* Returns the digit value of c in the given base (2..36), or -1 if c | |
| * isn't a digit in that base. */ | |
| static int | |
| _hb_digit_value (char c, int base) | |
| { | |
| int d; | |
| if ('0' <= c && c <= '9') d = c - '0'; | |
| else if ('a' <= c && c <= 'z') d = c - 'a' + 10; | |
| else if ('A' <= c && c <= 'Z') d = c - 'A' + 10; | |
| else return -1; | |
| return d < base ? d : -1; | |
| } | |
| /* Consumes digits in [p, pe) into *value (magnitude). Sets *overflow | |
| * if the value would exceed UINT64_MAX. Sets *any_digits if at least | |
| * one digit was consumed. Returns the position past the last digit. */ | |
| static const char * | |
| _hb_scan_digits (const char *p, const char *pe, int base, | |
| uint64_t *value, bool *overflow, bool *any_digits) | |
| { | |
| const uint64_t cutoff = UINT64_MAX / (unsigned) base; | |
| const uint64_t cutlim = UINT64_MAX % (unsigned) base; | |
| uint64_t v = 0; | |
| bool ov = false, any = false; | |
| for (; p < pe; p++) | |
| { | |
| int d = _hb_digit_value (*p, base); | |
| if (d < 0) break; | |
| any = true; | |
| if (v > cutoff || (v == cutoff && (uint64_t) d > cutlim)) | |
| ov = true; | |
| else | |
| v = v * (unsigned) base + (uint64_t) d; | |
| } | |
| *value = v; | |
| *overflow = ov; | |
| *any_digits = any; | |
| return p; | |
| } | |
| template<typename T, typename Postprocess> | |
| static bool | |
| _parse_number (const char **pp, const char *end, T *pv, | |
| bool whole_buffer, int base, Postprocess post) | |
| { | |
| const char *pend = end; | |
| const char *p = *pp; | |
| bool neg = false, any = false, overflow = false; | |
| while (p < pend && ISSPACE (*p)) p++; | |
| if (p < pend && (*p == '+' || *p == '-')) { neg = (*p == '-'); p++; } | |
| /* Skip an optional "0x"/"0X" prefix for base 16, but only when a hex | |
| * digit follows — "0x" alone parses as the digit 0, matching strtoul. */ | |
| if (base == 16 && p + 2 < pend && p[0] == '0' && | |
| (p[1] == 'x' || p[1] == 'X') && _hb_digit_value (p[2], 16) >= 0) | |
| p += 2; | |
| uint64_t v = 0; | |
| p = _hb_scan_digits (p, pend, base, &v, &overflow, &any); | |
| if (unlikely (!any || overflow)) return false; | |
| T out; | |
| if (unlikely (!post (v, neg, &out))) return false; | |
| if (unlikely (whole_buffer && p != pend)) return false; | |
| *pv = out; | |
| *pp = p; | |
| return true; | |
| } | |
| bool | |
| hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) | |
| { | |
| return _parse_number (pp, end, pv, whole_buffer, 10, | |
| [] (uint64_t v, bool neg, int *out) { | |
| /* INT_MAX = 2^31-1, INT_MIN magnitude = 2^31 */ | |
| uint64_t limit = neg ? (uint64_t) INT_MAX + 1 : (uint64_t) INT_MAX; | |
| if (unlikely (v > limit)) return false; | |
| *out = (int) (neg ? -(int64_t) v : (int64_t) v); | |
| return true; | |
| }); | |
| } | |
| bool | |
| hb_parse_uint (const char **pp, const char *end, unsigned *pv, | |
| bool whole_buffer, int base) | |
| { | |
| return _parse_number (pp, end, pv, whole_buffer, base, | |
| [] (uint64_t v, bool neg, unsigned *out) { | |
| if (unlikely (v > UINT_MAX)) return false; | |
| /* Negate in unsigned so -1 wraps to UINT_MAX, matching strtoul | |
| * truncated to unsigned. */ | |
| unsigned u = (unsigned) v; | |
| *out = neg ? (unsigned) (0u - u) : u; | |
| return true; | |
| }); | |
| } | |
| #endif /* HB_NUMBER_CC pacify */ | |
| #endif /* HB_NUMBER_CC guard */ |