Coverage Report

Created: 2026-10-17 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-number-parser.hh
Line
Count
Source
1
/*
2
 * Copyright © 2019-2026  Ebrahim Byagowi
3
 *
4
 *  This is part of HarfBuzz, a text shaping library.
5
 *
6
 * Permission is hereby granted, without written agreement and without
7
 * license or royalty fees, to use, copy, modify, and distribute this
8
 * software and its documentation for any purpose, provided that the
9
 * above copyright notice and the following two paragraphs appear in
10
 * all copies of this software.
11
 *
12
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16
 * DAMAGE.
17
 *
18
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23
 *
24
 */
25
26
#ifndef HB_NUMBER_PARSER_HH
27
#define HB_NUMBER_PARSER_HH
28
29
#include "hb.hh"
30
31
// Handles 0..308; anything larger overflows double and returns HUGE_VAL.
32
static inline double
33
_pow10 (unsigned exponent)
34
0
{
35
  // DBL_MAX is between 10^308 and 10^309
36
0
  if (unlikely (exponent > 308)) return HUGE_VAL;
37
38
0
  static const double _powers_of_10[] =
39
0
  {
40
0
    1.0e+256,
41
0
    1.0e+128,
42
0
    1.0e+64,
43
0
    1.0e+32,
44
0
    1.0e+16,
45
0
    1.0e+8,
46
0
    10000.,
47
0
    100.,
48
0
    10.
49
0
  };
50
0
  unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1);
51
0
  double result = 1;
52
0
  for (const double *power = _powers_of_10; mask; ++power, mask >>= 1)
53
0
    if (exponent & mask) result *= *power;
54
0
  return result;
55
0
}
56
57
struct _hb_fraction
58
{
59
  void add (int digit)
60
0
  {
61
0
    if (likely (fraction <= MAX_FRACT / 10))
62
0
    {
63
0
      fraction = fraction * 10. + digit;
64
0
      ++count;
65
0
    }
66
0
    else if (drop < 0) drop = digit;
67
0
    else if (digit != 0) sticky = true;
68
0
  }
69
70
  double get_value () const
71
0
  {
72
0
    double result = fraction;
73
    // Round-to-nearest-even using the recorded tail
74
0
    if (drop > 5 || (drop == 5 && ((((uint64_t) fraction) & 1) || sticky)))
75
0
      result += 1;
76
0
    return result / _pow10 (count);
77
0
  }
78
79
0
  bool is_empty () const { return !count; }
80
81
private:
82
  unsigned count = 0;
83
  double fraction = 0;
84
  int drop = -1; // drop is the first digit we couldn't accumulate
85
  bool sticky = false; // sticky is set when any later digit was nonzero.
86
  static constexpr uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */
87
};
88
89
// A variant of strtod that also gets end of buffer in its second argument.
90
//
91
// Grammar (with backtracking on partial exponents):
92
//
93
//   number  := sign? mantissa exponent?
94
//   sign    := '+' | '-'
95
//   mantissa:= digits ('.' digits?)?
96
//      | '.' digits
97
//   exponent:= ('e'|'E') sign? digits
98
//
99
// On success, *end_ptr is set just past the longest well-formed prefix.
100
// On no match, *end_ptr is left at the original p (matching strtod's
101
// "no conversion performed" behavior, so the caller's pointer is unchanged).
102
static inline double
103
hb_strtod (const char *p, const char **end_ptr /* IN/OUT */)
104
0
{
105
0
  const char *p_original = p;
106
0
  const char *pe = *end_ptr;
107
108
  // Skip leading whitespace
109
0
  while (p < pe && ISSPACE (*p))
110
0
    p++;
111
112
  // Sign
113
0
  bool neg = false;
114
0
  if (p < pe && (*p == '+' || *p == '-'))
115
0
  {
116
0
    neg = (*p == '-');
117
0
    p++;
118
0
  }
119
120
0
  double value = 0;
121
122
  // Integer and fraction
123
0
  {
124
0
    const char *int_start = p;
125
0
    while (p < pe && '0' <= *p && *p <= '9')
126
0
    {
127
0
      value = value * 10. + (*p - '0');
128
0
      p++;
129
0
    }
130
0
    bool has_int = p > int_start;
131
132
0
    _hb_fraction frac;
133
0
    if (p < pe && *p == '.')
134
0
    {
135
0
      p++;
136
0
      while (p < pe && '0' <= *p && *p <= '9')
137
0
      {
138
0
  frac.add (*p - '0');
139
0
  p++;
140
0
      }
141
0
    }
142
143
0
    if (!frac.is_empty ()) value += frac.get_value ();
144
0
    else if (!has_int)
145
0
    {
146
      // Nothing could be parsed
147
0
      *end_ptr = p_original;
148
0
      return .0;
149
0
    }
150
0
  }
151
152
  // Exponent
153
0
  unsigned exp = 0;
154
0
  bool exp_neg = false, exp_overflow = false;
155
0
  if (p < pe && (*p == 'e' || *p == 'E'))
156
0
  {
157
0
    constexpr unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */
158
0
    const char *before_exponent = p;
159
0
    p++;
160
0
    if (p < pe && (*p == '+' || *p == '-'))
161
0
    {
162
0
      exp_neg = (*p == '-');
163
0
      p++;
164
0
    }
165
166
0
    if (p < pe && '0' <= *p && *p <= '9')
167
0
      do
168
0
      {
169
0
  if (likely (exp * 10 + (*p - '0') <= MAX_EXP))
170
0
    exp = exp * 10 + (*p - '0');
171
0
  else
172
0
    exp_overflow = true;
173
0
  p++;
174
0
      } while (p < pe && '0' <= *p && *p <= '9');
175
0
    else
176
0
      p = before_exponent; // rewind
177
0
  }
178
0
  *end_ptr = p;
179
180
0
  if (neg) value *= -1.;
181
182
0
  if (unlikely (exp_overflow))
183
0
  {
184
0
    if (value == 0) return value;
185
0
    if (exp_neg)    return neg ? -.0 : .0;
186
0
    else            return neg ? -DBL_MAX : DBL_MAX;
187
0
  }
188
189
0
  if (exp)
190
0
  {
191
0
    if (exp_neg)
192
0
    {
193
      // 10^308 is the largest finite power of ten, so two reductions of 308 suffice.
194
0
      if (unlikely (exp > 308))
195
0
      {
196
0
  value /= 1e308;
197
0
  exp -= 308;
198
0
  if (unlikely (exp > 308))
199
0
  {
200
0
    value /= 1e308;
201
0
    exp -= 308;
202
0
  }
203
0
      }
204
0
      value /= _pow10 (exp);
205
0
    }
206
0
    else
207
0
      value *= _pow10 (exp);
208
0
  }
209
210
0
  return value;
211
0
}
212
213
#endif /* HB_NUMBER_PARSER_HH */