Coverage Report

Created: 2026-10-19 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pcre2/src/pcre2_script_run.c
Line
Count
Source
1
/*************************************************
2
*      Perl-Compatible Regular Expressions       *
3
*************************************************/
4
5
/* PCRE is a library of functions to support regular expressions whose syntax
6
and semantics are as close as possible to those of the Perl 5 language.
7
8
                       Written by Philip Hazel
9
     Original API code Copyright (c) 1997-2012 University of Cambridge
10
          New API code Copyright (c) 2016-2021 University of Cambridge
11
12
-----------------------------------------------------------------------------
13
Redistribution and use in source and binary forms, with or without
14
modification, are permitted provided that the following conditions are met:
15
16
    * Redistributions of source code must retain the above copyright notice,
17
      this list of conditions and the following disclaimer.
18
19
    * Redistributions in binary form must reproduce the above copyright
20
      notice, this list of conditions and the following disclaimer in the
21
      documentation and/or other materials provided with the distribution.
22
23
    * Neither the name of the University of Cambridge nor the names of its
24
      contributors may be used to endorse or promote products derived from
25
      this software without specific prior written permission.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
POSSIBILITY OF SUCH DAMAGE.
38
-----------------------------------------------------------------------------
39
*/
40
41
42
/* This module contains the function for checking a script run. */
43
44
45
#include "pcre2_internal.h"
46
47
48
49
/*************************************************
50
*                Check script run                *
51
*************************************************/
52
53
/* A script run is conceptually a sequence of characters all in the same
54
Unicode script. However, it isn't quite that simple. There are special rules
55
for scripts that are commonly used together, and also special rules for digits.
56
This function implements the appropriate checks, which is possible only when
57
PCRE2 is compiled with Unicode support. The function returns TRUE if there is
58
no Unicode support; however, it should never be called in that circumstance
59
because an error is given by pcre2_compile() if a script run is called for in a
60
version of PCRE2 compiled without Unicode support.
61
62
Arguments:
63
  pgr       point to the first character
64
  endptr    point after the last character
65
  utf       TRUE if in UTF mode
66
67
Returns:    TRUE if this is a valid script run
68
*/
69
70
/* These are states in the checking process. */
71
72
enum {
73
  SCRIPT_UNSET,       // Requirement as yet unknown
74
  SCRIPT_MAP,         // Bitmap contains acceptable scripts
75
  SCRIPT_HANPENDING,  // Have had only Han characters
76
  SCRIPT_HANHIRAKATA, // Expect Han or Hirikata
77
  SCRIPT_HANBOPOMOFO, // Expect Han or Bopomofo
78
  SCRIPT_HANHANGUL    // Expect Han or Hangul
79
};
80
81
3.79M
#define UCD_MAPSIZE  (ucp_Unknown / 32 + 1)
82
9.47M
#define FULL_MAPSIZE (ucp_Script_Count / 32 + 1)
83
84
BOOL
85
PRIV(script_run)(PCRE2_SPTR ptr, PCRE2_SPTR endptr, BOOL utf)
86
662k
{
87
662k
#ifdef SUPPORT_UNICODE
88
662k
  uint32_t require_state = SCRIPT_UNSET;
89
662k
  uint32_t require_map[FULL_MAPSIZE];
90
662k
  uint32_t map[FULL_MAPSIZE];
91
662k
  uint32_t require_digitset = 0;
92
662k
  uint32_t c;
93
94
#if PCRE2_CODE_UNIT_WIDTH == 32
95
  (void)utf; // Avoid compiler warning
96
#endif
97
98
  /* Any string containing fewer than 2 characters is a valid script run. */
99
100
662k
  if (ptr >= endptr)
101
161k
    return TRUE;
102
500k
  GETCHARINCTEST(c, ptr);
103
500k
  if (ptr >= endptr)
104
31.7k
    return TRUE;
105
106
  /* Initialize the require map. This is a full-size bitmap that has a bit for
107
  every script, as opposed to the maps in ucd_script_sets, which only have bits
108
  for scripts less than ucp_Unknown - those that appear in script extension
109
  lists. */
110
111
3.28M
  for (int i = 0; i < FULL_MAPSIZE; i++)
112
2.81M
    require_map[i] = 0;
113
114
  /* Scan strings of two or more characters, checking the Unicode characteristics
115
  of each code point. There is special code for scripts that can be combined with
116
  characters from the Han Chinese script. This may be used in conjunction with
117
  four other scripts in these combinations:
118
119
  . Han with Hiragana and Katakana is allowed (for Japanese).
120
  . Han with Bopomofo is allowed (for Taiwanese Mandarin).
121
  . Han with Hangul is allowed (for Korean).
122
123
  If the first significant character's script is one of the four, the required
124
  script type is immediately known. However, if the first significant
125
  character's script is Han, we have to keep checking for a non-Han character.
126
  Hence the SCRIPT_HANPENDING state. */
127
128
468k
  for (;;)
129
4.32M
  {
130
4.32M
    const ucd_record *ucd = GET_UCD(c);
131
4.32M
    uint32_t script = ucd->script;
132
133
    /* If the script is Unknown, the string is not a valid script run. Such
134
    characters can only form script runs of length one (see test above). */
135
136
4.32M
    if (script == ucp_Unknown)
137
65.8k
      return FALSE;
138
139
    /* A character without any script extensions whose script is Inherited or
140
    Common is always accepted with any script. If there are extensions, the
141
    following processing happens for all scripts. */
142
143
4.25M
    if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))
144
1.26M
    {
145
1.26M
      BOOL OK;
146
147
      /* Set up a full-sized map for this character that can include bits for all
148
      scripts. Copy the scriptx map for this character (which covers those
149
      scripts that appear in script extension lists), set the remaining values to
150
      zero, and then, except for Common or Inherited, add this script's bit to
151
      the map. */
152
153
1.26M
      memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));
154
1.26M
      memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));
155
1.26M
      if (script != ucp_Common && script != ucp_Inherited)
156
1.15M
        MAPSET(map, script);
157
158
      /* Handle the different checking states */
159
160
1.26M
      switch (require_state)
161
1.26M
      {
162
        /* First significant character - it might follow Common or Inherited
163
        characters that do not have any script extensions. */
164
165
398k
      case SCRIPT_UNSET:
166
398k
        switch (script)
167
398k
        {
168
64.7k
        case ucp_Han:
169
64.7k
          require_state = SCRIPT_HANPENDING;
170
64.7k
          break;
171
172
5.89k
        case ucp_Hiragana:
173
13.2k
        case ucp_Katakana:
174
13.2k
          require_state = SCRIPT_HANHIRAKATA;
175
13.2k
          break;
176
177
9.45k
        case ucp_Bopomofo:
178
9.45k
          require_state = SCRIPT_HANBOPOMOFO;
179
9.45k
          break;
180
181
14.6k
        case ucp_Hangul:
182
14.6k
          require_state = SCRIPT_HANHANGUL;
183
14.6k
          break;
184
185
295k
        default:
186
295k
          memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));
187
295k
          require_state = SCRIPT_MAP;
188
295k
          break;
189
398k
        }
190
191
398k
        break;
192
193
        /* The first significant character was Han. An inspection of the Unicode
194
        11.0.0 files shows that there are the following types of Script Extension
195
        list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul
196
        scripts:
197
198
        . Bopomofo + Han
199
        . Han + Hiragana + Katakana
200
        . Hiragana + Katakana
201
        . Bopopmofo + Hangul + Han + Hiragana + Katakana
202
203
        The following code tries to make sense of this. */
204
205
398k
#define FOUND_BOPOMOFO 1
206
398k
#define FOUND_HIRAGANA 2
207
398k
#define FOUND_KATAKANA 4
208
398k
#define FOUND_HANGUL   8
209
210
398k
      case SCRIPT_HANPENDING:
211
155k
        if (script != ucp_Han) // Another Han does nothing
212
75.4k
        {
213
75.4k
          uint32_t chspecial = 0;
214
215
75.4k
          if (MAPBIT(map, ucp_Bopomofo) != 0)
216
18.0k
            chspecial |= FOUND_BOPOMOFO;
217
75.4k
          if (MAPBIT(map, ucp_Hiragana) != 0)
218
26.6k
            chspecial |= FOUND_HIRAGANA;
219
75.4k
          if (MAPBIT(map, ucp_Katakana) != 0)
220
32.1k
            chspecial |= FOUND_KATAKANA;
221
75.4k
          if (MAPBIT(map, ucp_Hangul) != 0)
222
24.7k
            chspecial |= FOUND_HANGUL;
223
224
75.4k
          if (chspecial == 0)
225
27.2k
            return FALSE; // Not allowed with Han
226
227
48.1k
          if (chspecial == FOUND_BOPOMOFO)
228
3.32k
            require_state = SCRIPT_HANBOPOMOFO;
229
44.8k
          else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA))
230
9.42k
            require_state = SCRIPT_HANHIRAKATA;
231
232
          /* Otherwise this character must be allowed with all of them, so remain
233
          in the pending state. */
234
48.1k
        }
235
236
128k
        break;
237
238
        /* Previously encountered one of the "with Han" scripts. Check that
239
        this character is appropriate. */
240
241
128k
      case SCRIPT_HANHIRAKATA:
242
45.4k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0)
243
21.7k
          return FALSE;
244
23.7k
        break;
245
246
26.8k
      case SCRIPT_HANBOPOMOFO:
247
26.8k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0)
248
11.6k
          return FALSE;
249
15.1k
        break;
250
251
26.1k
      case SCRIPT_HANHANGUL:
252
26.1k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0)
253
12.6k
          return FALSE;
254
13.4k
        break;
255
256
        /* Previously encountered one or more characters that are allowed with a
257
        list of scripts. */
258
259
611k
      case SCRIPT_MAP:
260
611k
        OK = FALSE;
261
262
1.28M
        for (int i = 0; i < FULL_MAPSIZE; i++)
263
1.17M
        {
264
1.17M
          if ((require_map[i] & map[i]) != 0)
265
502k
          {
266
502k
            OK = TRUE;
267
502k
            break;
268
502k
          }
269
1.17M
        }
270
271
611k
        if (!OK)
272
109k
          return FALSE;
273
274
        /* The rest of the string must be in this script, but we have to
275
        allow for the Han complications. */
276
277
502k
        switch (script)
278
502k
        {
279
5.57k
        case ucp_Han:
280
5.57k
          require_state = SCRIPT_HANPENDING;
281
5.57k
          break;
282
283
3.37k
        case ucp_Hiragana:
284
9.18k
        case ucp_Katakana:
285
9.18k
          require_state = SCRIPT_HANHIRAKATA;
286
9.18k
          break;
287
288
4.74k
        case ucp_Bopomofo:
289
4.74k
          require_state = SCRIPT_HANBOPOMOFO;
290
4.74k
          break;
291
292
4.25k
        case ucp_Hangul:
293
4.25k
          require_state = SCRIPT_HANHANGUL;
294
4.25k
          break;
295
296
          /* Compute the intersection of the required list of scripts and the
297
          allowed scripts for this character. */
298
299
478k
        default:
300
3.35M
          for (int i = 0; i < FULL_MAPSIZE; i++)
301
2.87M
            require_map[i] &= map[i];
302
478k
          break;
303
502k
        }
304
305
502k
        break;
306
307
        /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */
308
502k
      default:
309
0
        PCRE2_DEBUG_UNREACHABLE();
310
0
        break;
311
        /* LCOV_EXCL_STOP */
312
1.26M
      }
313
1.26M
    } // End checking character's script and extensions.
314
315
    /* The character is in an acceptable script. We must now ensure that all
316
    decimal digits in the string come from the same set. Some scripts (e.g.
317
    Common, Arabic) have more than one set of decimal digits. This code does
318
    not allow mixing sets, even within the same script. The vector called
319
    PRIV(ucd_digit_sets)[] contains, in its first element, the number of
320
    following elements, and then, in ascending order, the code points of the
321
    '9' characters in every set of 10 digits. Each set is identified by the
322
    offset in the vector of its '9' character. An initial check of the first
323
    value picks up ASCII digits quickly. Otherwise, a binary chop is used. */
324
325
4.07M
    if (ucd->chartype == ucp_Nd)
326
287k
    {
327
287k
      uint32_t digitset;
328
329
287k
      if (c <= PRIV(ucd_digit_sets)[1])
330
266k
      {
331
266k
        digitset = 1;
332
266k
      }
333
21.5k
      else
334
21.5k
      {
335
21.5k
        int mid;
336
21.5k
        int bot = 1;
337
21.5k
        int top = PRIV(ucd_digit_sets)[0];
338
21.5k
        for (;;)
339
152k
        {
340
152k
          if (top <= bot + 1) // <= rather than == is paranoia
341
21.5k
          {
342
21.5k
            digitset = top;
343
21.5k
            break;
344
21.5k
          }
345
346
131k
          mid = (top + bot) / 2;
347
131k
          if (c <= PRIV(ucd_digit_sets)[mid])
348
75.8k
            top = mid;
349
55.2k
          else
350
55.2k
            bot = mid;
351
131k
        }
352
21.5k
      }
353
354
      /* A required value of 0 means "unset". */
355
356
287k
      if (require_digitset == 0)
357
53.9k
        require_digitset = digitset;
358
233k
      else if (digitset != require_digitset)
359
6.56k
        return FALSE;
360
287k
    } // End digit handling
361
362
    /* If we haven't yet got to the end, pick up the next character. */
363
364
4.07M
    if (ptr >= endptr)
365
214k
      return TRUE;
366
3.85M
    GETCHARINCTEST(c, ptr);
367
3.85M
  } // End checking loop
368
369
#else  /* NOT SUPPORT_UNICODE */
370
  (void)ptr;
371
  (void)endptr;
372
  (void)utf;
373
  return TRUE;
374
#endif /* SUPPORT_UNICODE */
375
468k
}
_pcre2_script_run_8
Line
Count
Source
86
249k
{
87
249k
#ifdef SUPPORT_UNICODE
88
249k
  uint32_t require_state = SCRIPT_UNSET;
89
249k
  uint32_t require_map[FULL_MAPSIZE];
90
249k
  uint32_t map[FULL_MAPSIZE];
91
249k
  uint32_t require_digitset = 0;
92
249k
  uint32_t c;
93
94
#if PCRE2_CODE_UNIT_WIDTH == 32
95
  (void)utf; // Avoid compiler warning
96
#endif
97
98
  /* Any string containing fewer than 2 characters is a valid script run. */
99
100
249k
  if (ptr >= endptr)
101
50.3k
    return TRUE;
102
199k
  GETCHARINCTEST(c, ptr);
103
199k
  if (ptr >= endptr)
104
12.7k
    return TRUE;
105
106
  /* Initialize the require map. This is a full-size bitmap that has a bit for
107
  every script, as opposed to the maps in ucd_script_sets, which only have bits
108
  for scripts less than ucp_Unknown - those that appear in script extension
109
  lists. */
110
111
1.30M
  for (int i = 0; i < FULL_MAPSIZE; i++)
112
1.11M
    require_map[i] = 0;
113
114
  /* Scan strings of two or more characters, checking the Unicode characteristics
115
  of each code point. There is special code for scripts that can be combined with
116
  characters from the Han Chinese script. This may be used in conjunction with
117
  four other scripts in these combinations:
118
119
  . Han with Hiragana and Katakana is allowed (for Japanese).
120
  . Han with Bopomofo is allowed (for Taiwanese Mandarin).
121
  . Han with Hangul is allowed (for Korean).
122
123
  If the first significant character's script is one of the four, the required
124
  script type is immediately known. However, if the first significant
125
  character's script is Han, we have to keep checking for a non-Han character.
126
  Hence the SCRIPT_HANPENDING state. */
127
128
186k
  for (;;)
129
2.48M
  {
130
2.48M
    const ucd_record *ucd = GET_UCD(c);
131
2.48M
    uint32_t script = ucd->script;
132
133
    /* If the script is Unknown, the string is not a valid script run. Such
134
    characters can only form script runs of length one (see test above). */
135
136
2.48M
    if (script == ucp_Unknown)
137
5.00k
      return FALSE;
138
139
    /* A character without any script extensions whose script is Inherited or
140
    Common is always accepted with any script. If there are extensions, the
141
    following processing happens for all scripts. */
142
143
2.48M
    if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))
144
606k
    {
145
606k
      BOOL OK;
146
147
      /* Set up a full-sized map for this character that can include bits for all
148
      scripts. Copy the scriptx map for this character (which covers those
149
      scripts that appear in script extension lists), set the remaining values to
150
      zero, and then, except for Common or Inherited, add this script's bit to
151
      the map. */
152
153
606k
      memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));
154
606k
      memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));
155
606k
      if (script != ucp_Common && script != ucp_Inherited)
156
577k
        MAPSET(map, script);
157
158
      /* Handle the different checking states */
159
160
606k
      switch (require_state)
161
606k
      {
162
        /* First significant character - it might follow Common or Inherited
163
        characters that do not have any script extensions. */
164
165
161k
      case SCRIPT_UNSET:
166
161k
        switch (script)
167
161k
        {
168
9.08k
        case ucp_Han:
169
9.08k
          require_state = SCRIPT_HANPENDING;
170
9.08k
          break;
171
172
2.89k
        case ucp_Hiragana:
173
5.83k
        case ucp_Katakana:
174
5.83k
          require_state = SCRIPT_HANHIRAKATA;
175
5.83k
          break;
176
177
1.85k
        case ucp_Bopomofo:
178
1.85k
          require_state = SCRIPT_HANBOPOMOFO;
179
1.85k
          break;
180
181
2.06k
        case ucp_Hangul:
182
2.06k
          require_state = SCRIPT_HANHANGUL;
183
2.06k
          break;
184
185
142k
        default:
186
142k
          memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));
187
142k
          require_state = SCRIPT_MAP;
188
142k
          break;
189
161k
        }
190
191
161k
        break;
192
193
        /* The first significant character was Han. An inspection of the Unicode
194
        11.0.0 files shows that there are the following types of Script Extension
195
        list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul
196
        scripts:
197
198
        . Bopomofo + Han
199
        . Han + Hiragana + Katakana
200
        . Hiragana + Katakana
201
        . Bopopmofo + Hangul + Han + Hiragana + Katakana
202
203
        The following code tries to make sense of this. */
204
205
161k
#define FOUND_BOPOMOFO 1
206
161k
#define FOUND_HIRAGANA 2
207
161k
#define FOUND_KATAKANA 4
208
161k
#define FOUND_HANGUL   8
209
210
161k
      case SCRIPT_HANPENDING:
211
10.7k
        if (script != ucp_Han) // Another Han does nothing
212
9.48k
        {
213
9.48k
          uint32_t chspecial = 0;
214
215
9.48k
          if (MAPBIT(map, ucp_Bopomofo) != 0)
216
1.75k
            chspecial |= FOUND_BOPOMOFO;
217
9.48k
          if (MAPBIT(map, ucp_Hiragana) != 0)
218
3.67k
            chspecial |= FOUND_HIRAGANA;
219
9.48k
          if (MAPBIT(map, ucp_Katakana) != 0)
220
4.11k
            chspecial |= FOUND_KATAKANA;
221
9.48k
          if (MAPBIT(map, ucp_Hangul) != 0)
222
1.72k
            chspecial |= FOUND_HANGUL;
223
224
9.48k
          if (chspecial == 0)
225
3.77k
            return FALSE; // Not allowed with Han
226
227
5.71k
          if (chspecial == FOUND_BOPOMOFO)
228
803
            require_state = SCRIPT_HANBOPOMOFO;
229
4.91k
          else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA))
230
2.70k
            require_state = SCRIPT_HANHIRAKATA;
231
232
          /* Otherwise this character must be allowed with all of them, so remain
233
          in the pending state. */
234
5.71k
        }
235
236
7.01k
        break;
237
238
        /* Previously encountered one of the "with Han" scripts. Check that
239
        this character is appropriate. */
240
241
11.6k
      case SCRIPT_HANHIRAKATA:
242
11.6k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0)
243
9.28k
          return FALSE;
244
2.32k
        break;
245
246
3.33k
      case SCRIPT_HANBOPOMOFO:
247
3.33k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0)
248
1.97k
          return FALSE;
249
1.36k
        break;
250
251
3.94k
      case SCRIPT_HANHANGUL:
252
3.94k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0)
253
2.23k
          return FALSE;
254
1.70k
        break;
255
256
        /* Previously encountered one or more characters that are allowed with a
257
        list of scripts. */
258
259
416k
      case SCRIPT_MAP:
260
416k
        OK = FALSE;
261
262
634k
        for (int i = 0; i < FULL_MAPSIZE; i++)
263
599k
        {
264
599k
          if ((require_map[i] & map[i]) != 0)
265
380k
          {
266
380k
            OK = TRUE;
267
380k
            break;
268
380k
          }
269
599k
        }
270
271
416k
        if (!OK)
272
35.6k
          return FALSE;
273
274
        /* The rest of the string must be in this script, but we have to
275
        allow for the Han complications. */
276
277
380k
        switch (script)
278
380k
        {
279
1.22k
        case ucp_Han:
280
1.22k
          require_state = SCRIPT_HANPENDING;
281
1.22k
          break;
282
283
1.54k
        case ucp_Hiragana:
284
5.09k
        case ucp_Katakana:
285
5.09k
          require_state = SCRIPT_HANHIRAKATA;
286
5.09k
          break;
287
288
727
        case ucp_Bopomofo:
289
727
          require_state = SCRIPT_HANBOPOMOFO;
290
727
          break;
291
292
2.72k
        case ucp_Hangul:
293
2.72k
          require_state = SCRIPT_HANHANGUL;
294
2.72k
          break;
295
296
          /* Compute the intersection of the required list of scripts and the
297
          allowed scripts for this character. */
298
299
370k
        default:
300
2.59M
          for (int i = 0; i < FULL_MAPSIZE; i++)
301
2.22M
            require_map[i] &= map[i];
302
370k
          break;
303
380k
        }
304
305
380k
        break;
306
307
        /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */
308
380k
      default:
309
0
        PCRE2_DEBUG_UNREACHABLE();
310
0
        break;
311
        /* LCOV_EXCL_STOP */
312
606k
      }
313
606k
    } // End checking character's script and extensions.
314
315
    /* The character is in an acceptable script. We must now ensure that all
316
    decimal digits in the string come from the same set. Some scripts (e.g.
317
    Common, Arabic) have more than one set of decimal digits. This code does
318
    not allow mixing sets, even within the same script. The vector called
319
    PRIV(ucd_digit_sets)[] contains, in its first element, the number of
320
    following elements, and then, in ascending order, the code points of the
321
    '9' characters in every set of 10 digits. Each set is identified by the
322
    offset in the vector of its '9' character. An initial check of the first
323
    value picks up ASCII digits quickly. Otherwise, a binary chop is used. */
324
325
2.42M
    if (ucd->chartype == ucp_Nd)
326
259k
    {
327
259k
      uint32_t digitset;
328
329
259k
      if (c <= PRIV(ucd_digit_sets)[1])
330
253k
      {
331
253k
        digitset = 1;
332
253k
      }
333
5.88k
      else
334
5.88k
      {
335
5.88k
        int mid;
336
5.88k
        int bot = 1;
337
5.88k
        int top = PRIV(ucd_digit_sets)[0];
338
5.88k
        for (;;)
339
42.1k
        {
340
42.1k
          if (top <= bot + 1) // <= rather than == is paranoia
341
5.88k
          {
342
5.88k
            digitset = top;
343
5.88k
            break;
344
5.88k
          }
345
346
36.2k
          mid = (top + bot) / 2;
347
36.2k
          if (c <= PRIV(ucd_digit_sets)[mid])
348
24.5k
            top = mid;
349
11.6k
          else
350
11.6k
            bot = mid;
351
36.2k
        }
352
5.88k
      }
353
354
      /* A required value of 0 means "unset". */
355
356
259k
      if (require_digitset == 0)
357
35.2k
        require_digitset = digitset;
358
223k
      else if (digitset != require_digitset)
359
1.94k
        return FALSE;
360
259k
    } // End digit handling
361
362
    /* If we haven't yet got to the end, pick up the next character. */
363
364
2.42M
    if (ptr >= endptr)
365
126k
      return TRUE;
366
2.30M
    GETCHARINCTEST(c, ptr);
367
2.30M
  } // End checking loop
368
369
#else  /* NOT SUPPORT_UNICODE */
370
  (void)ptr;
371
  (void)endptr;
372
  (void)utf;
373
  return TRUE;
374
#endif /* SUPPORT_UNICODE */
375
186k
}
_pcre2_script_run_32
Line
Count
Source
86
64.8k
{
87
64.8k
#ifdef SUPPORT_UNICODE
88
64.8k
  uint32_t require_state = SCRIPT_UNSET;
89
64.8k
  uint32_t require_map[FULL_MAPSIZE];
90
64.8k
  uint32_t map[FULL_MAPSIZE];
91
64.8k
  uint32_t require_digitset = 0;
92
64.8k
  uint32_t c;
93
94
64.8k
#if PCRE2_CODE_UNIT_WIDTH == 32
95
64.8k
  (void)utf; // Avoid compiler warning
96
64.8k
#endif
97
98
  /* Any string containing fewer than 2 characters is a valid script run. */
99
100
64.8k
  if (ptr >= endptr)
101
21.9k
    return TRUE;
102
42.9k
  GETCHARINCTEST(c, ptr);
103
42.9k
  if (ptr >= endptr)
104
4.70k
    return TRUE;
105
106
  /* Initialize the require map. This is a full-size bitmap that has a bit for
107
  every script, as opposed to the maps in ucd_script_sets, which only have bits
108
  for scripts less than ucp_Unknown - those that appear in script extension
109
  lists. */
110
111
267k
  for (int i = 0; i < FULL_MAPSIZE; i++)
112
229k
    require_map[i] = 0;
113
114
  /* Scan strings of two or more characters, checking the Unicode characteristics
115
  of each code point. There is special code for scripts that can be combined with
116
  characters from the Han Chinese script. This may be used in conjunction with
117
  four other scripts in these combinations:
118
119
  . Han with Hiragana and Katakana is allowed (for Japanese).
120
  . Han with Bopomofo is allowed (for Taiwanese Mandarin).
121
  . Han with Hangul is allowed (for Korean).
122
123
  If the first significant character's script is one of the four, the required
124
  script type is immediately known. However, if the first significant
125
  character's script is Han, we have to keep checking for a non-Han character.
126
  Hence the SCRIPT_HANPENDING state. */
127
128
38.2k
  for (;;)
129
171k
  {
130
171k
    const ucd_record *ucd = GET_UCD(c);
131
171k
    uint32_t script = ucd->script;
132
133
    /* If the script is Unknown, the string is not a valid script run. Such
134
    characters can only form script runs of length one (see test above). */
135
136
171k
    if (script == ucp_Unknown)
137
13.4k
      return FALSE;
138
139
    /* A character without any script extensions whose script is Inherited or
140
    Common is always accepted with any script. If there are extensions, the
141
    following processing happens for all scripts. */
142
143
157k
    if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))
144
77.6k
    {
145
77.6k
      BOOL OK;
146
147
      /* Set up a full-sized map for this character that can include bits for all
148
      scripts. Copy the scriptx map for this character (which covers those
149
      scripts that appear in script extension lists), set the remaining values to
150
      zero, and then, except for Common or Inherited, add this script's bit to
151
      the map. */
152
153
77.6k
      memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));
154
77.6k
      memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));
155
77.6k
      if (script != ucp_Common && script != ucp_Inherited)
156
69.6k
        MAPSET(map, script);
157
158
      /* Handle the different checking states */
159
160
77.6k
      switch (require_state)
161
77.6k
      {
162
        /* First significant character - it might follow Common or Inherited
163
        characters that do not have any script extensions. */
164
165
32.8k
      case SCRIPT_UNSET:
166
32.8k
        switch (script)
167
32.8k
        {
168
7.28k
        case ucp_Han:
169
7.28k
          require_state = SCRIPT_HANPENDING;
170
7.28k
          break;
171
172
870
        case ucp_Hiragana:
173
1.93k
        case ucp_Katakana:
174
1.93k
          require_state = SCRIPT_HANHIRAKATA;
175
1.93k
          break;
176
177
1.45k
        case ucp_Bopomofo:
178
1.45k
          require_state = SCRIPT_HANBOPOMOFO;
179
1.45k
          break;
180
181
1.32k
        case ucp_Hangul:
182
1.32k
          require_state = SCRIPT_HANHANGUL;
183
1.32k
          break;
184
185
20.8k
        default:
186
20.8k
          memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));
187
20.8k
          require_state = SCRIPT_MAP;
188
20.8k
          break;
189
32.8k
        }
190
191
32.8k
        break;
192
193
        /* The first significant character was Han. An inspection of the Unicode
194
        11.0.0 files shows that there are the following types of Script Extension
195
        list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul
196
        scripts:
197
198
        . Bopomofo + Han
199
        . Han + Hiragana + Katakana
200
        . Hiragana + Katakana
201
        . Bopopmofo + Hangul + Han + Hiragana + Katakana
202
203
        The following code tries to make sense of this. */
204
205
32.8k
#define FOUND_BOPOMOFO 1
206
32.8k
#define FOUND_HIRAGANA 2
207
32.8k
#define FOUND_KATAKANA 4
208
32.8k
#define FOUND_HANGUL   8
209
210
32.8k
      case SCRIPT_HANPENDING:
211
7.69k
        if (script != ucp_Han) // Another Han does nothing
212
5.40k
        {
213
5.40k
          uint32_t chspecial = 0;
214
215
5.40k
          if (MAPBIT(map, ucp_Bopomofo) != 0)
216
1.44k
            chspecial |= FOUND_BOPOMOFO;
217
5.40k
          if (MAPBIT(map, ucp_Hiragana) != 0)
218
1.19k
            chspecial |= FOUND_HIRAGANA;
219
5.40k
          if (MAPBIT(map, ucp_Katakana) != 0)
220
1.46k
            chspecial |= FOUND_KATAKANA;
221
5.40k
          if (MAPBIT(map, ucp_Hangul) != 0)
222
1.29k
            chspecial |= FOUND_HANGUL;
223
224
5.40k
          if (chspecial == 0)
225
1.72k
            return FALSE; // Not allowed with Han
226
227
3.68k
          if (chspecial == FOUND_BOPOMOFO)
228
1.17k
            require_state = SCRIPT_HANBOPOMOFO;
229
2.51k
          else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA))
230
906
            require_state = SCRIPT_HANHIRAKATA;
231
232
          /* Otherwise this character must be allowed with all of them, so remain
233
          in the pending state. */
234
3.68k
        }
235
236
5.97k
        break;
237
238
        /* Previously encountered one of the "with Han" scripts. Check that
239
        this character is appropriate. */
240
241
5.97k
      case SCRIPT_HANHIRAKATA:
242
4.38k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0)
243
2.61k
          return FALSE;
244
1.76k
        break;
245
246
4.26k
      case SCRIPT_HANBOPOMOFO:
247
4.26k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0)
248
2.32k
          return FALSE;
249
1.94k
        break;
250
251
2.17k
      case SCRIPT_HANHANGUL:
252
2.17k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0)
253
1.35k
          return FALSE;
254
819
        break;
255
256
        /* Previously encountered one or more characters that are allowed with a
257
        list of scripts. */
258
259
26.2k
      case SCRIPT_MAP:
260
26.2k
        OK = FALSE;
261
262
66.7k
        for (int i = 0; i < FULL_MAPSIZE; i++)
263
60.7k
        {
264
60.7k
          if ((require_map[i] & map[i]) != 0)
265
20.2k
          {
266
20.2k
            OK = TRUE;
267
20.2k
            break;
268
20.2k
          }
269
60.7k
        }
270
271
26.2k
        if (!OK)
272
6.01k
          return FALSE;
273
274
        /* The rest of the string must be in this script, but we have to
275
        allow for the Han complications. */
276
277
20.2k
        switch (script)
278
20.2k
        {
279
718
        case ucp_Han:
280
718
          require_state = SCRIPT_HANPENDING;
281
718
          break;
282
283
878
        case ucp_Hiragana:
284
1.66k
        case ucp_Katakana:
285
1.66k
          require_state = SCRIPT_HANHIRAKATA;
286
1.66k
          break;
287
288
609
        case ucp_Bopomofo:
289
609
          require_state = SCRIPT_HANBOPOMOFO;
290
609
          break;
291
292
618
        case ucp_Hangul:
293
618
          require_state = SCRIPT_HANHANGUL;
294
618
          break;
295
296
          /* Compute the intersection of the required list of scripts and the
297
          allowed scripts for this character. */
298
299
16.6k
        default:
300
116k
          for (int i = 0; i < FULL_MAPSIZE; i++)
301
99.9k
            require_map[i] &= map[i];
302
16.6k
          break;
303
20.2k
        }
304
305
20.2k
        break;
306
307
        /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */
308
20.2k
      default:
309
0
        PCRE2_DEBUG_UNREACHABLE();
310
0
        break;
311
        /* LCOV_EXCL_STOP */
312
77.6k
      }
313
77.6k
    } // End checking character's script and extensions.
314
315
    /* The character is in an acceptable script. We must now ensure that all
316
    decimal digits in the string come from the same set. Some scripts (e.g.
317
    Common, Arabic) have more than one set of decimal digits. This code does
318
    not allow mixing sets, even within the same script. The vector called
319
    PRIV(ucd_digit_sets)[] contains, in its first element, the number of
320
    following elements, and then, in ascending order, the code points of the
321
    '9' characters in every set of 10 digits. Each set is identified by the
322
    offset in the vector of its '9' character. An initial check of the first
323
    value picks up ASCII digits quickly. Otherwise, a binary chop is used. */
324
325
143k
    if (ucd->chartype == ucp_Nd)
326
4.84k
    {
327
4.84k
      uint32_t digitset;
328
329
4.84k
      if (c <= PRIV(ucd_digit_sets)[1])
330
2.04k
      {
331
2.04k
        digitset = 1;
332
2.04k
      }
333
2.80k
      else
334
2.80k
      {
335
2.80k
        int mid;
336
2.80k
        int bot = 1;
337
2.80k
        int top = PRIV(ucd_digit_sets)[0];
338
2.80k
        for (;;)
339
20.4k
        {
340
20.4k
          if (top <= bot + 1) // <= rather than == is paranoia
341
2.80k
          {
342
2.80k
            digitset = top;
343
2.80k
            break;
344
2.80k
          }
345
346
17.6k
          mid = (top + bot) / 2;
347
17.6k
          if (c <= PRIV(ucd_digit_sets)[mid])
348
6.62k
            top = mid;
349
11.0k
          else
350
11.0k
            bot = mid;
351
17.6k
        }
352
2.80k
      }
353
354
      /* A required value of 0 means "unset". */
355
356
4.84k
      if (require_digitset == 0)
357
3.29k
        require_digitset = digitset;
358
1.54k
      else if (digitset != require_digitset)
359
732
        return FALSE;
360
4.84k
    } // End digit handling
361
362
    /* If we haven't yet got to the end, pick up the next character. */
363
364
142k
    if (ptr >= endptr)
365
10.0k
      return TRUE;
366
132k
    GETCHARINCTEST(c, ptr);
367
132k
  } // End checking loop
368
369
#else  /* NOT SUPPORT_UNICODE */
370
  (void)ptr;
371
  (void)endptr;
372
  (void)utf;
373
  return TRUE;
374
#endif /* SUPPORT_UNICODE */
375
38.2k
}
_pcre2_script_run_16
Line
Count
Source
86
348k
{
87
348k
#ifdef SUPPORT_UNICODE
88
348k
  uint32_t require_state = SCRIPT_UNSET;
89
348k
  uint32_t require_map[FULL_MAPSIZE];
90
348k
  uint32_t map[FULL_MAPSIZE];
91
348k
  uint32_t require_digitset = 0;
92
348k
  uint32_t c;
93
94
#if PCRE2_CODE_UNIT_WIDTH == 32
95
  (void)utf; // Avoid compiler warning
96
#endif
97
98
  /* Any string containing fewer than 2 characters is a valid script run. */
99
100
348k
  if (ptr >= endptr)
101
89.3k
    return TRUE;
102
258k
  GETCHARINCTEST(c, ptr);
103
258k
  if (ptr >= endptr)
104
14.2k
    return TRUE;
105
106
  /* Initialize the require map. This is a full-size bitmap that has a bit for
107
  every script, as opposed to the maps in ucd_script_sets, which only have bits
108
  for scripts less than ucp_Unknown - those that appear in script extension
109
  lists. */
110
111
1.71M
  for (int i = 0; i < FULL_MAPSIZE; i++)
112
1.46M
    require_map[i] = 0;
113
114
  /* Scan strings of two or more characters, checking the Unicode characteristics
115
  of each code point. There is special code for scripts that can be combined with
116
  characters from the Han Chinese script. This may be used in conjunction with
117
  four other scripts in these combinations:
118
119
  . Han with Hiragana and Katakana is allowed (for Japanese).
120
  . Han with Bopomofo is allowed (for Taiwanese Mandarin).
121
  . Han with Hangul is allowed (for Korean).
122
123
  If the first significant character's script is one of the four, the required
124
  script type is immediately known. However, if the first significant
125
  character's script is Han, we have to keep checking for a non-Han character.
126
  Hence the SCRIPT_HANPENDING state. */
127
128
244k
  for (;;)
129
1.66M
  {
130
1.66M
    const ucd_record *ucd = GET_UCD(c);
131
1.66M
    uint32_t script = ucd->script;
132
133
    /* If the script is Unknown, the string is not a valid script run. Such
134
    characters can only form script runs of length one (see test above). */
135
136
1.66M
    if (script == ucp_Unknown)
137
47.3k
      return FALSE;
138
139
    /* A character without any script extensions whose script is Inherited or
140
    Common is always accepted with any script. If there are extensions, the
141
    following processing happens for all scripts. */
142
143
1.61M
    if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))
144
579k
    {
145
579k
      BOOL OK;
146
147
      /* Set up a full-sized map for this character that can include bits for all
148
      scripts. Copy the scriptx map for this character (which covers those
149
      scripts that appear in script extension lists), set the remaining values to
150
      zero, and then, except for Common or Inherited, add this script's bit to
151
      the map. */
152
153
579k
      memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));
154
579k
      memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));
155
579k
      if (script != ucp_Common && script != ucp_Inherited)
156
509k
        MAPSET(map, script);
157
158
      /* Handle the different checking states */
159
160
579k
      switch (require_state)
161
579k
      {
162
        /* First significant character - it might follow Common or Inherited
163
        characters that do not have any script extensions. */
164
165
204k
      case SCRIPT_UNSET:
166
204k
        switch (script)
167
204k
        {
168
48.4k
        case ucp_Han:
169
48.4k
          require_state = SCRIPT_HANPENDING;
170
48.4k
          break;
171
172
2.13k
        case ucp_Hiragana:
173
5.50k
        case ucp_Katakana:
174
5.50k
          require_state = SCRIPT_HANHIRAKATA;
175
5.50k
          break;
176
177
6.14k
        case ucp_Bopomofo:
178
6.14k
          require_state = SCRIPT_HANBOPOMOFO;
179
6.14k
          break;
180
181
11.2k
        case ucp_Hangul:
182
11.2k
          require_state = SCRIPT_HANHANGUL;
183
11.2k
          break;
184
185
132k
        default:
186
132k
          memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));
187
132k
          require_state = SCRIPT_MAP;
188
132k
          break;
189
204k
        }
190
191
204k
        break;
192
193
        /* The first significant character was Han. An inspection of the Unicode
194
        11.0.0 files shows that there are the following types of Script Extension
195
        list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul
196
        scripts:
197
198
        . Bopomofo + Han
199
        . Han + Hiragana + Katakana
200
        . Hiragana + Katakana
201
        . Bopopmofo + Hangul + Han + Hiragana + Katakana
202
203
        The following code tries to make sense of this. */
204
205
204k
#define FOUND_BOPOMOFO 1
206
204k
#define FOUND_HIRAGANA 2
207
204k
#define FOUND_KATAKANA 4
208
204k
#define FOUND_HANGUL   8
209
210
204k
      case SCRIPT_HANPENDING:
211
137k
        if (script != ucp_Han) // Another Han does nothing
212
60.5k
        {
213
60.5k
          uint32_t chspecial = 0;
214
215
60.5k
          if (MAPBIT(map, ucp_Bopomofo) != 0)
216
14.8k
            chspecial |= FOUND_BOPOMOFO;
217
60.5k
          if (MAPBIT(map, ucp_Hiragana) != 0)
218
21.8k
            chspecial |= FOUND_HIRAGANA;
219
60.5k
          if (MAPBIT(map, ucp_Katakana) != 0)
220
26.6k
            chspecial |= FOUND_KATAKANA;
221
60.5k
          if (MAPBIT(map, ucp_Hangul) != 0)
222
21.7k
            chspecial |= FOUND_HANGUL;
223
224
60.5k
          if (chspecial == 0)
225
21.7k
            return FALSE; // Not allowed with Han
226
227
38.7k
          if (chspecial == FOUND_BOPOMOFO)
228
1.35k
            require_state = SCRIPT_HANBOPOMOFO;
229
37.4k
          else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA))
230
5.80k
            require_state = SCRIPT_HANHIRAKATA;
231
232
          /* Otherwise this character must be allowed with all of them, so remain
233
          in the pending state. */
234
38.7k
        }
235
236
115k
        break;
237
238
        /* Previously encountered one of the "with Han" scripts. Check that
239
        this character is appropriate. */
240
241
115k
      case SCRIPT_HANHIRAKATA:
242
29.4k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0)
243
9.81k
          return FALSE;
244
19.6k
        break;
245
246
19.6k
      case SCRIPT_HANBOPOMOFO:
247
19.2k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0)
248
7.36k
          return FALSE;
249
11.8k
        break;
250
251
19.9k
      case SCRIPT_HANHANGUL:
252
19.9k
        if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0)
253
9.09k
          return FALSE;
254
10.8k
        break;
255
256
        /* Previously encountered one or more characters that are allowed with a
257
        list of scripts. */
258
259
169k
      case SCRIPT_MAP:
260
169k
        OK = FALSE;
261
262
583k
        for (int i = 0; i < FULL_MAPSIZE; i++)
263
515k
        {
264
515k
          if ((require_map[i] & map[i]) != 0)
265
101k
          {
266
101k
            OK = TRUE;
267
101k
            break;
268
101k
          }
269
515k
        }
270
271
169k
        if (!OK)
272
67.5k
          return FALSE;
273
274
        /* The rest of the string must be in this script, but we have to
275
        allow for the Han complications. */
276
277
101k
        switch (script)
278
101k
        {
279
3.63k
        case ucp_Han:
280
3.63k
          require_state = SCRIPT_HANPENDING;
281
3.63k
          break;
282
283
955
        case ucp_Hiragana:
284
2.42k
        case ucp_Katakana:
285
2.42k
          require_state = SCRIPT_HANHIRAKATA;
286
2.42k
          break;
287
288
3.41k
        case ucp_Bopomofo:
289
3.41k
          require_state = SCRIPT_HANBOPOMOFO;
290
3.41k
          break;
291
292
914
        case ucp_Hangul:
293
914
          require_state = SCRIPT_HANHANGUL;
294
914
          break;
295
296
          /* Compute the intersection of the required list of scripts and the
297
          allowed scripts for this character. */
298
299
91.5k
        default:
300
641k
          for (int i = 0; i < FULL_MAPSIZE; i++)
301
549k
            require_map[i] &= map[i];
302
91.5k
          break;
303
101k
        }
304
305
101k
        break;
306
307
        /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */
308
101k
      default:
309
0
        PCRE2_DEBUG_UNREACHABLE();
310
0
        break;
311
        /* LCOV_EXCL_STOP */
312
579k
      }
313
579k
    } // End checking character's script and extensions.
314
315
    /* The character is in an acceptable script. We must now ensure that all
316
    decimal digits in the string come from the same set. Some scripts (e.g.
317
    Common, Arabic) have more than one set of decimal digits. This code does
318
    not allow mixing sets, even within the same script. The vector called
319
    PRIV(ucd_digit_sets)[] contains, in its first element, the number of
320
    following elements, and then, in ascending order, the code points of the
321
    '9' characters in every set of 10 digits. Each set is identified by the
322
    offset in the vector of its '9' character. An initial check of the first
323
    value picks up ASCII digits quickly. Otherwise, a binary chop is used. */
324
325
1.50M
    if (ucd->chartype == ucp_Nd)
326
23.5k
    {
327
23.5k
      uint32_t digitset;
328
329
23.5k
      if (c <= PRIV(ucd_digit_sets)[1])
330
10.7k
      {
331
10.7k
        digitset = 1;
332
10.7k
      }
333
12.8k
      else
334
12.8k
      {
335
12.8k
        int mid;
336
12.8k
        int bot = 1;
337
12.8k
        int top = PRIV(ucd_digit_sets)[0];
338
12.8k
        for (;;)
339
89.9k
        {
340
89.9k
          if (top <= bot + 1) // <= rather than == is paranoia
341
12.8k
          {
342
12.8k
            digitset = top;
343
12.8k
            break;
344
12.8k
          }
345
346
77.1k
          mid = (top + bot) / 2;
347
77.1k
          if (c <= PRIV(ucd_digit_sets)[mid])
348
44.6k
            top = mid;
349
32.5k
          else
350
32.5k
            bot = mid;
351
77.1k
        }
352
12.8k
      }
353
354
      /* A required value of 0 means "unset". */
355
356
23.5k
      if (require_digitset == 0)
357
15.4k
        require_digitset = digitset;
358
8.16k
      else if (digitset != require_digitset)
359
3.88k
        return FALSE;
360
23.5k
    } // End digit handling
361
362
    /* If we haven't yet got to the end, pick up the next character. */
363
364
1.49M
    if (ptr >= endptr)
365
77.5k
      return TRUE;
366
1.42M
    GETCHARINCTEST(c, ptr);
367
1.42M
  } // End checking loop
368
369
#else  /* NOT SUPPORT_UNICODE */
370
  (void)ptr;
371
  (void)endptr;
372
  (void)utf;
373
  return TRUE;
374
#endif /* SUPPORT_UNICODE */
375
244k
}
376
377
/* End of pcre2_script_run.c */