Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libgit2/deps/pcre2/pcre2_xclass.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-2024 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 two internal functions that are used to match
43
OP_XCLASS and OP_ECLASS. It is used by pcre2_auto_possessify() and by both
44
pcre2_match() and pcre2_dfa_match(). */
45
46
47
#include "pcre2_internal.h"
48
49
50
51
/*************************************************
52
*       Match character against an XCLASS        *
53
*************************************************/
54
55
/* This function is called to match a character against an extended class that
56
might contain codepoints above 255 and/or Unicode properties.
57
58
Arguments:
59
  c           the character
60
  data        points to the flag code unit of the XCLASS data
61
  utf         TRUE if in UTF mode
62
63
Returns:      TRUE if character matches, else FALSE
64
*/
65
66
BOOL
67
PRIV(xclass)(uint32_t c, PCRE2_SPTR data, const uint8_t *char_lists_end, BOOL utf)
68
53.2k
{
69
/* Update PRIV(update_classbits) when this function is changed. */
70
53.2k
PCRE2_UCHAR t;
71
53.2k
BOOL not_negated = (*data & XCL_NOT) == 0;
72
53.2k
uint32_t type, max_index, min_index, value;
73
53.2k
const uint8_t *next_char;
74
75
53.2k
#if PCRE2_CODE_UNIT_WIDTH == 8
76
/* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */
77
53.2k
utf = TRUE;
78
53.2k
#endif
79
80
/* Code points < 256 are matched against a bitmap, if one is present. */
81
82
53.2k
if ((*data++ & XCL_MAP) != 0)
83
11.1k
  {
84
11.1k
  if (c < 256)
85
862
    return (((const uint8_t *)data)[c/8] & (1u << (c&7))) != 0;
86
  /* Skip bitmap. */
87
10.3k
  data += 32 / sizeof(PCRE2_UCHAR);
88
10.3k
  }
89
90
/* Match against the list of Unicode properties. We won't ever
91
encounter XCL_PROP or XCL_NOTPROP when UTF support is not compiled. */
92
52.4k
#ifdef SUPPORT_UNICODE
93
52.4k
if (*data == XCL_PROP || *data == XCL_NOTPROP)
94
42.1k
  {
95
  /* The UCD record is the same for all properties. */
96
42.1k
  const ucd_record *prop = GET_UCD(c);
97
98
42.1k
  do
99
44.3k
    {
100
44.3k
    int chartype;
101
44.3k
    BOOL isprop = (*data++) == XCL_PROP;
102
44.3k
    BOOL ok;
103
104
44.3k
    switch(*data)
105
44.3k
      {
106
3.47k
      case PT_LAMP:
107
3.47k
      chartype = prop->chartype;
108
3.47k
      if ((chartype == ucp_Lu || chartype == ucp_Ll ||
109
1.30k
           chartype == ucp_Lt) == isprop) return not_negated;
110
2.76k
      break;
111
112
6.71k
      case PT_GC:
113
6.71k
      if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == isprop)
114
2.99k
        return not_negated;
115
3.72k
      break;
116
117
3.72k
      case PT_PC:
118
1.02k
      if ((data[1] == prop->chartype) == isprop) return not_negated;
119
524
      break;
120
121
524
      case PT_SC:
122
514
      if ((data[1] == prop->script) == isprop) return not_negated;
123
238
      break;
124
125
1.45k
      case PT_SCX:
126
1.45k
      ok = (data[1] == prop->script ||
127
633
            MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), data[1]) != 0);
128
1.45k
      if (ok == isprop) return not_negated;
129
634
      break;
130
131
1.03k
      case PT_ALNUM:
132
1.03k
      chartype = prop->chartype;
133
1.03k
      if ((PRIV(ucp_gentype)[chartype] == ucp_L ||
134
529
           PRIV(ucp_gentype)[chartype] == ucp_N) == isprop)
135
610
        return not_negated;
136
427
      break;
137
138
      /* Perl space used to exclude VT, but from Perl 5.18 it is included,
139
      which means that Perl space and POSIX space are now identical. PCRE
140
      was changed at release 8.34. */
141
142
8.80k
      case PT_SPACE:    /* Perl space */
143
11.9k
      case PT_PXSPACE:  /* POSIX space */
144
11.9k
      switch(c)
145
11.9k
        {
146
64.5k
        HSPACE_CASES:
147
64.5k
        VSPACE_CASES:
148
53.9k
        if (isprop) return not_negated;
149
5.34k
        break;
150
151
5.34k
        default:
152
2.51k
        if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == isprop)
153
803
          return not_negated;
154
1.71k
        break;
155
11.9k
        }
156
7.05k
      break;
157
158
7.05k
      case PT_WORD:
159
2.85k
      chartype = prop->chartype;
160
2.85k
      if ((PRIV(ucp_gentype)[chartype] == ucp_L ||
161
1.24k
           PRIV(ucp_gentype)[chartype] == ucp_N ||
162
910
           chartype == ucp_Mn || chartype == ucp_Pc) == isprop)
163
1.09k
        return not_negated;
164
1.76k
      break;
165
166
3.95k
      case PT_UCNC:
167
3.95k
      if (c < 0xa0)
168
2.35k
        {
169
2.35k
        if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
170
1.35k
             c == CHAR_GRAVE_ACCENT) == isprop)
171
1.11k
          return not_negated;
172
2.35k
        }
173
1.59k
      else
174
1.59k
        {
175
1.59k
        if ((c < 0xd800 || c > 0xdfff) == isprop)
176
814
          return not_negated;
177
1.59k
        }
178
2.02k
      break;
179
180
2.02k
      case PT_BIDICL:
181
495
      if ((UCD_BIDICLASS_PROP(prop) == data[1]) == isprop)
182
292
        return not_negated;
183
203
      break;
184
185
540
      case PT_BOOL:
186
540
      ok = MAPBIT(PRIV(ucd_boolprop_sets) +
187
540
        UCD_BPROPS_PROP(prop), data[1]) != 0;
188
540
      if (ok == isprop) return not_negated;
189
273
      break;
190
191
      /* The following three properties can occur only in an XCLASS, as there
192
      is no \p or \P coding for them. */
193
194
      /* Graphic character. Implement this as not Z (space or separator) and
195
      not C (other), except for Cf (format) with a few exceptions. This seems
196
      to be what Perl does. The exceptional characters are:
197
198
      U+061C           Arabic Letter Mark
199
      U+180E           Mongolian Vowel Separator
200
      U+2066 - U+2069  Various "isolate"s
201
      */
202
203
2.28k
      case PT_PXGRAPH:
204
2.28k
      chartype = prop->chartype;
205
2.28k
      if ((PRIV(ucp_gentype)[chartype] != ucp_Z &&
206
2.02k
            (PRIV(ucp_gentype)[chartype] != ucp_C ||
207
1.71k
              (chartype == ucp_Cf &&
208
1.22k
                c != 0x061c && c != 0x180e && (c < 0x2066 || c > 0x2069))
209
2.02k
         )) == isprop)
210
1.17k
        return not_negated;
211
1.10k
      break;
212
213
      /* Printable character: same as graphic, with the addition of Zs, i.e.
214
      not Zl and not Zp, and U+180E. */
215
216
1.69k
      case PT_PXPRINT:
217
1.69k
      chartype = prop->chartype;
218
1.69k
      if ((chartype != ucp_Zl &&
219
1.40k
           chartype != ucp_Zp &&
220
1.07k
            (PRIV(ucp_gentype)[chartype] != ucp_C ||
221
781
              (chartype == ucp_Cf &&
222
482
                c != 0x061c && (c < 0x2066 || c > 0x2069))
223
1.07k
         )) == isprop)
224
529
        return not_negated;
225
1.16k
      break;
226
227
      /* Punctuation: all Unicode punctuation, plus ASCII characters that
228
      Unicode treats as symbols rather than punctuation, for Perl
229
      compatibility (these are $+<=>^`|~). */
230
231
1.16k
      case PT_PXPUNCT:
232
956
      chartype = prop->chartype;
233
956
      if ((PRIV(ucp_gentype)[chartype] == ucp_P ||
234
707
            (c < 128 && PRIV(ucp_gentype)[chartype] == ucp_S)) == isprop)
235
592
        return not_negated;
236
364
      break;
237
238
      /* Perl has two sets of hex digits */
239
240
5.44k
      case PT_PXXDIGIT:
241
5.44k
      if (((c >= CHAR_0 && c <= CHAR_9) ||
242
5.14k
           (c >= CHAR_A && c <= CHAR_F) ||
243
4.78k
           (c >= CHAR_a && c <= CHAR_f) ||
244
4.38k
           (c >= 0xff10 && c <= 0xff19) ||  /* Fullwidth digits */
245
3.83k
           (c >= 0xff21 && c <= 0xff26) ||  /* Fullwidth letters */
246
3.63k
           (c >= 0xff41 && c <= 0xff46)) == isprop)
247
1.81k
        return not_negated;
248
3.63k
      break;
249
250
      /* This should never occur, but compilers may mutter if there is no
251
      default. */
252
253
      /* LCOV_EXCL_START */
254
3.63k
      default:
255
0
      PCRE2_DEBUG_UNREACHABLE();
256
0
      return FALSE;
257
      /* LCOV_EXCL_STOP */
258
44.3k
      }
259
260
25.8k
    data += 2;
261
25.8k
    }
262
42.1k
  while (*data == XCL_PROP || *data == XCL_NOTPROP);
263
42.1k
  }
264
#else
265
  (void)utf;  /* Avoid compiler warning */
266
#endif  /* SUPPORT_UNICODE */
267
268
/* Match against large chars or ranges that end with a large char. */
269
33.9k
if (*data < XCL_LIST)
270
26.2k
  {
271
26.7k
  while ((t = *data++) != XCL_END)
272
2.82k
    {
273
2.82k
    uint32_t x, y;
274
275
2.82k
#ifdef SUPPORT_UNICODE
276
2.82k
    if (utf)
277
2.82k
      {
278
2.82k
      GETCHARINC(x, data); /* macro generates multiple statements */
279
2.82k
      }
280
0
    else
281
0
#endif
282
0
      x = *data++;
283
284
2.82k
    if (t == XCL_SINGLE)
285
1.12k
      {
286
      /* Since character ranges follow the properties, and they are
287
      sorted, early return is possible for all characters <= x. */
288
1.12k
      if (c <= x) return (c == x) ? not_negated : !not_negated;
289
343
      continue;
290
1.12k
      }
291
292
1.69k
    PCRE2_ASSERT(t == XCL_RANGE);
293
1.69k
#ifdef SUPPORT_UNICODE
294
1.69k
    if (utf)
295
1.69k
      {
296
1.69k
      GETCHARINC(y, data); /* macro generates multiple statements */
297
1.69k
      }
298
0
    else
299
0
#endif
300
0
      y = *data++;
301
302
    /* Since character ranges follow the properties, and they are
303
    sorted, early return is possible for all characters <= y. */
304
1.69k
    if (c <= y) return (c >= x) ? not_negated : !not_negated;
305
1.69k
    }
306
307
23.9k
  return !not_negated;   /* char did not match */
308
26.2k
  }
309
310
7.74k
#if PCRE2_CODE_UNIT_WIDTH == 8
311
7.74k
type = (uint32_t)(data[0] << 8) | data[1];
312
7.74k
data += 2;
313
#else
314
type = data[0];
315
data++;
316
#endif  /* CODE_UNIT_WIDTH */
317
318
/* Align characters. */
319
7.74k
next_char = char_lists_end - (GET(data, 0) << 1);
320
7.74k
type &= XCL_TYPE_MASK;
321
322
/* Alignment check. */
323
7.74k
PCRE2_ASSERT(((uintptr_t)next_char & 0x1) == 0);
324
325
7.74k
if (c >= XCL_CHAR_LIST_HIGH_16_START)
326
4.25k
  {
327
4.25k
  max_index = type & XCL_ITEM_COUNT_MASK;
328
4.25k
  if (max_index == XCL_ITEM_COUNT_MASK)
329
3.99k
    {
330
3.99k
    max_index = *(const uint16_t*)next_char;
331
3.99k
    PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);
332
3.99k
    next_char += 2;
333
3.99k
    }
334
335
4.25k
  next_char += max_index << 1;
336
4.25k
  type >>= XCL_TYPE_BIT_LEN;
337
4.25k
  }
338
339
7.74k
if (c < XCL_CHAR_LIST_LOW_32_START)
340
4.55k
  {
341
4.55k
  max_index = type & XCL_ITEM_COUNT_MASK;
342
343
4.55k
  c = (uint16_t)((c << XCL_CHAR_SHIFT) | XCL_CHAR_END);
344
345
4.55k
  if (max_index == XCL_ITEM_COUNT_MASK)
346
3.74k
    {
347
3.74k
    max_index = *(const uint16_t*)next_char;
348
3.74k
    PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);
349
3.74k
    next_char += 2;
350
3.74k
    }
351
352
4.55k
  if (max_index == 0 || c < *(const uint16_t*)next_char)
353
2.27k
    return ((type & XCL_BEGIN_WITH_RANGE) != 0) == not_negated;
354
355
2.28k
  min_index = 0;
356
2.28k
  value = ((const uint16_t*)next_char)[--max_index];
357
2.28k
  if (c >= value)
358
1.02k
    return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;
359
360
1.25k
  max_index--;
361
362
  /* Binary search of a range. */
363
4.11k
  while (TRUE)
364
4.11k
    {
365
4.11k
    uint32_t mid_index = (min_index + max_index) >> 1;
366
4.11k
    value = ((const uint16_t*)next_char)[mid_index];
367
368
4.11k
    if (c < value)
369
1.20k
      max_index = mid_index - 1;
370
2.91k
    else if (((const uint16_t*)next_char)[mid_index + 1] <= c)
371
1.65k
      min_index = mid_index + 1;
372
1.25k
    else
373
1.25k
      return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;
374
4.11k
    }
375
1.25k
  }
376
377
/* Skip the 16 bit ranges. */
378
3.18k
max_index = type & XCL_ITEM_COUNT_MASK;
379
3.18k
if (max_index == XCL_ITEM_COUNT_MASK)
380
302
  {
381
302
  max_index = *(const uint16_t*)next_char;
382
302
  PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);
383
302
  next_char += 2;
384
302
  }
385
386
3.18k
next_char += (max_index << 1);
387
3.18k
type >>= XCL_TYPE_BIT_LEN;
388
389
/* Alignment check. */
390
3.18k
PCRE2_ASSERT(((uintptr_t)next_char & 0x3) == 0);
391
392
3.18k
max_index = type & XCL_ITEM_COUNT_MASK;
393
394
#if PCRE2_CODE_UNIT_WIDTH == 32
395
if (c >= XCL_CHAR_LIST_HIGH_32_START)
396
  {
397
  if (max_index == XCL_ITEM_COUNT_MASK)
398
    {
399
    max_index = *(const uint32_t*)next_char;
400
    PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);
401
    next_char += 4;
402
    }
403
404
  next_char += max_index << 2;
405
  type >>= XCL_TYPE_BIT_LEN;
406
  max_index = type & XCL_ITEM_COUNT_MASK;
407
  }
408
#endif
409
410
3.18k
c = (uint32_t)((c << XCL_CHAR_SHIFT) | XCL_CHAR_END);
411
412
3.18k
if (max_index == XCL_ITEM_COUNT_MASK)
413
906
  {
414
906
  max_index = *(const uint32_t*)next_char;
415
906
  next_char += 4;
416
906
  }
417
418
3.18k
if (max_index == 0 || c < *(const uint32_t*)next_char)
419
821
  return ((type & XCL_BEGIN_WITH_RANGE) != 0) == not_negated;
420
421
2.36k
min_index = 0;
422
2.36k
value = ((const uint32_t*)next_char)[--max_index];
423
2.36k
if (c >= value)
424
701
  return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;
425
426
1.66k
max_index--;
427
428
/* Binary search of a range. */
429
3.24k
while (TRUE)
430
3.24k
  {
431
3.24k
  uint32_t mid_index = (min_index + max_index) >> 1;
432
3.24k
  value = ((const uint32_t*)next_char)[mid_index];
433
434
3.24k
  if (c < value)
435
826
    max_index = mid_index - 1;
436
2.42k
  else if (((const uint32_t*)next_char)[mid_index + 1] <= c)
437
759
    min_index = mid_index + 1;
438
1.66k
  else
439
1.66k
    return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;
440
3.24k
  }
441
1.66k
}
442
443
444
445
/*************************************************
446
*       Match character against an ECLASS        *
447
*************************************************/
448
449
/* This function is called to match a character against an extended class
450
used for describing characters using boolean operations on sets.
451
452
Arguments:
453
  c           the character
454
  data_start  points to the start of the ECLASS data
455
  data_end    points one-past-the-last of the ECLASS data
456
  utf         TRUE if in UTF mode
457
458
Returns:      TRUE if character matches, else FALSE
459
*/
460
461
BOOL
462
PRIV(eclass)(uint32_t c, PCRE2_SPTR data_start, PCRE2_SPTR data_end,
463
  const uint8_t *char_lists_end, BOOL utf)
464
4.62k
{
465
4.62k
PCRE2_SPTR ptr = data_start;
466
4.62k
PCRE2_UCHAR flags;
467
4.62k
uint32_t stack = 0;
468
4.62k
int stack_depth = 0;
469
470
4.62k
PCRE2_ASSERT(data_start < data_end);
471
4.62k
flags = *ptr++;
472
4.62k
PCRE2_ASSERT((flags & ECL_MAP) == 0 ||
473
4.62k
             (data_end - ptr) >= 32 / (int)sizeof(PCRE2_UCHAR));
474
475
/* Code points < 256 are matched against a bitmap, if one is present.
476
Otherwise all codepoints are checked later. */
477
478
4.62k
if ((flags & ECL_MAP) != 0)
479
2.53k
  {
480
2.53k
  if (c < 256)
481
1.44k
    return (((const uint8_t *)ptr)[c/8] & (1u << (c&7))) != 0;
482
483
  /* Skip the bitmap. */
484
1.09k
  ptr += 32 / sizeof(PCRE2_UCHAR);
485
1.09k
  }
486
487
/* Do a little loop, until we reach the end of the ECLASS. */
488
14.3k
while (ptr < data_end)
489
11.1k
  {
490
11.1k
  switch (*ptr)
491
11.1k
    {
492
1.45k
    case ECL_AND:
493
1.45k
    ++ptr;
494
1.45k
    stack = (stack >> 1) & (stack | ~(uint32_t)1u);
495
1.45k
    PCRE2_ASSERT(stack_depth >= 2);
496
1.45k
    --stack_depth;
497
1.45k
    break;
498
499
718
    case ECL_OR:
500
718
    ++ptr;
501
718
    stack = (stack >> 1) | (stack & (uint32_t)1u);
502
718
    PCRE2_ASSERT(stack_depth >= 2);
503
718
    --stack_depth;
504
718
    break;
505
506
1.43k
    case ECL_XOR:
507
1.43k
    ++ptr;
508
1.43k
    stack = (stack >> 1) ^ (stack & (uint32_t)1u);
509
1.43k
    PCRE2_ASSERT(stack_depth >= 2);
510
1.43k
    --stack_depth;
511
1.43k
    break;
512
513
764
    case ECL_NOT:
514
764
    ++ptr;
515
764
    stack ^= (uint32_t)1u;
516
764
    PCRE2_ASSERT(stack_depth >= 1);
517
764
    break;
518
519
6.79k
    case ECL_XCLASS:
520
6.79k
      {
521
6.79k
      uint32_t matched = PRIV(xclass)(c, ptr + 1 + LINK_SIZE, char_lists_end, utf);
522
523
6.79k
      ptr += GET(ptr, 1);
524
6.79k
      stack = (stack << 1) | matched;
525
6.79k
      ++stack_depth;
526
6.79k
      break;
527
0
      }
528
529
    /* This should never occur, but compilers may mutter if there is no
530
    default. */
531
532
    /* LCOV_EXCL_START */
533
0
    default:
534
0
    PCRE2_DEBUG_UNREACHABLE();
535
0
    return FALSE;
536
    /* LCOV_EXCL_STOP */
537
11.1k
    }
538
11.1k
  }
539
540
3.18k
PCRE2_ASSERT(stack_depth == 1);
541
3.18k
(void)stack_depth;  /* Ignore unused variable, if assertions are disabled. */
542
543
/* The final bit left on the stack now holds the match result. */
544
3.18k
return (stack & 1u) != 0;
545
3.18k
}
546
547
/* End of pcre2_xclass.c */