LCOV - code coverage report
Current view: top level - src/backend/tsearch - regis.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 100 128 78.1 %
Date: 2024-04-19 08:11:23 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * regis.c
       4             :  *      Fast regex subset
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/tsearch/regis.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "tsearch/dicts/regis.h"
      18             : #include "tsearch/ts_locale.h"
      19             : 
      20             : #define RS_IN_ONEOF 1
      21             : #define RS_IN_ONEOF_IN  2
      22             : #define RS_IN_NONEOF    3
      23             : #define RS_IN_WAIT  4
      24             : 
      25             : 
      26             : /*
      27             :  * Test whether a regex is of the subset supported here.
      28             :  * Keep this in sync with RS_compile!
      29             :  */
      30             : bool
      31         804 : RS_isRegis(const char *str)
      32             : {
      33         804 :     int         state = RS_IN_WAIT;
      34         804 :     const char *c = str;
      35             : 
      36        4872 :     while (*c)
      37             :     {
      38        4200 :         if (state == RS_IN_WAIT)
      39             :         {
      40        1064 :             if (t_isalpha(c))
      41             :                  /* okay */ ;
      42         836 :             else if (t_iseq(c, '['))
      43         704 :                 state = RS_IN_ONEOF;
      44             :             else
      45         132 :                 return false;
      46             :         }
      47        3136 :         else if (state == RS_IN_ONEOF)
      48             :         {
      49         704 :             if (t_iseq(c, '^'))
      50         704 :                 state = RS_IN_NONEOF;
      51           0 :             else if (t_isalpha(c))
      52           0 :                 state = RS_IN_ONEOF_IN;
      53             :             else
      54           0 :                 return false;
      55             :         }
      56        2432 :         else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
      57             :         {
      58        2432 :             if (t_isalpha(c))
      59             :                  /* okay */ ;
      60         704 :             else if (t_iseq(c, ']'))
      61         704 :                 state = RS_IN_WAIT;
      62             :             else
      63           0 :                 return false;
      64             :         }
      65             :         else
      66           0 :             elog(ERROR, "internal error in RS_isRegis: state %d", state);
      67        4068 :         c += pg_mblen(c);
      68             :     }
      69             : 
      70         672 :     return (state == RS_IN_WAIT);
      71             : }
      72             : 
      73             : static RegisNode *
      74         768 : newRegisNode(RegisNode *prev, int len)
      75             : {
      76             :     RegisNode  *ptr;
      77             : 
      78         768 :     ptr = (RegisNode *) palloc0(RNHDRSZ + len + 1);
      79         768 :     if (prev)
      80          96 :         prev->next = ptr;
      81         768 :     return ptr;
      82             : }
      83             : 
      84             : void
      85         672 : RS_compile(Regis *r, bool issuffix, const char *str)
      86             : {
      87         672 :     int         len = strlen(str);
      88         672 :     int         state = RS_IN_WAIT;
      89         672 :     const char *c = str;
      90         672 :     RegisNode  *ptr = NULL;
      91             : 
      92         672 :     memset(r, 0, sizeof(Regis));
      93         672 :     r->issuffix = (issuffix) ? 1 : 0;
      94             : 
      95        4352 :     while (*c)
      96             :     {
      97        3680 :         if (state == RS_IN_WAIT)
      98             :         {
      99         768 :             if (t_isalpha(c))
     100             :             {
     101          96 :                 if (ptr)
     102          96 :                     ptr = newRegisNode(ptr, len);
     103             :                 else
     104           0 :                     ptr = r->node = newRegisNode(NULL, len);
     105          96 :                 COPYCHAR(ptr->data, c);
     106          96 :                 ptr->type = RSF_ONEOF;
     107          96 :                 ptr->len = pg_mblen(c);
     108             :             }
     109         672 :             else if (t_iseq(c, '['))
     110             :             {
     111         672 :                 if (ptr)
     112           0 :                     ptr = newRegisNode(ptr, len);
     113             :                 else
     114         672 :                     ptr = r->node = newRegisNode(NULL, len);
     115         672 :                 ptr->type = RSF_ONEOF;
     116         672 :                 state = RS_IN_ONEOF;
     117             :             }
     118             :             else                /* shouldn't get here */
     119           0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     120             :         }
     121        2912 :         else if (state == RS_IN_ONEOF)
     122             :         {
     123         672 :             if (t_iseq(c, '^'))
     124             :             {
     125         672 :                 ptr->type = RSF_NONEOF;
     126         672 :                 state = RS_IN_NONEOF;
     127             :             }
     128           0 :             else if (t_isalpha(c))
     129             :             {
     130           0 :                 COPYCHAR(ptr->data, c);
     131           0 :                 ptr->len = pg_mblen(c);
     132           0 :                 state = RS_IN_ONEOF_IN;
     133             :             }
     134             :             else                /* shouldn't get here */
     135           0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     136             :         }
     137        2240 :         else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
     138             :         {
     139        2240 :             if (t_isalpha(c))
     140             :             {
     141        1568 :                 COPYCHAR(ptr->data + ptr->len, c);
     142        1568 :                 ptr->len += pg_mblen(c);
     143             :             }
     144         672 :             else if (t_iseq(c, ']'))
     145         672 :                 state = RS_IN_WAIT;
     146             :             else                /* shouldn't get here */
     147           0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     148             :         }
     149             :         else
     150           0 :             elog(ERROR, "internal error in RS_compile: state %d", state);
     151        3680 :         c += pg_mblen(c);
     152             :     }
     153             : 
     154         672 :     if (state != RS_IN_WAIT)    /* shouldn't get here */
     155           0 :         elog(ERROR, "invalid regis pattern: \"%s\"", str);
     156             : 
     157         672 :     ptr = r->node;
     158        1440 :     while (ptr)
     159             :     {
     160         768 :         r->nchar++;
     161         768 :         ptr = ptr->next;
     162             :     }
     163         672 : }
     164             : 
     165             : void
     166           0 : RS_free(Regis *r)
     167             : {
     168           0 :     RegisNode  *ptr = r->node,
     169             :                *tmp;
     170             : 
     171           0 :     while (ptr)
     172             :     {
     173           0 :         tmp = ptr->next;
     174           0 :         pfree(ptr);
     175           0 :         ptr = tmp;
     176             :     }
     177             : 
     178           0 :     r->node = NULL;
     179           0 : }
     180             : 
     181             : static bool
     182         708 : mb_strchr(char *str, char *c)
     183             : {
     184             :     int         clen,
     185             :                 plen,
     186             :                 i;
     187         708 :     char       *ptr = str;
     188         708 :     bool        res = false;
     189             : 
     190         708 :     clen = pg_mblen(c);
     191        2328 :     while (*ptr && !res)
     192             :     {
     193        1620 :         plen = pg_mblen(ptr);
     194        1620 :         if (plen == clen)
     195             :         {
     196        1620 :             i = plen;
     197        1620 :             res = true;
     198        1656 :             while (i--)
     199        1620 :                 if (*(ptr + i) != *(c + i))
     200             :                 {
     201        1584 :                     res = false;
     202        1584 :                     break;
     203             :                 }
     204             :         }
     205             : 
     206        1620 :         ptr += plen;
     207             :     }
     208             : 
     209         708 :     return res;
     210             : }
     211             : 
     212             : bool
     213         708 : RS_execute(Regis *r, char *str)
     214             : {
     215         708 :     RegisNode  *ptr = r->node;
     216         708 :     char       *c = str;
     217         708 :     int         len = 0;
     218             : 
     219        4680 :     while (*c)
     220             :     {
     221        3972 :         len++;
     222        3972 :         c += pg_mblen(c);
     223             :     }
     224             : 
     225         708 :     if (len < r->nchar)
     226          36 :         return 0;
     227             : 
     228         672 :     c = str;
     229         672 :     if (r->issuffix)
     230             :     {
     231         672 :         len -= r->nchar;
     232        3936 :         while (len-- > 0)
     233        3264 :             c += pg_mblen(c);
     234             :     }
     235             : 
     236             : 
     237        1380 :     while (ptr)
     238             :     {
     239         708 :         switch (ptr->type)
     240             :         {
     241          36 :             case RSF_ONEOF:
     242          36 :                 if (!mb_strchr((char *) ptr->data, c))
     243           0 :                     return false;
     244          36 :                 break;
     245         672 :             case RSF_NONEOF:
     246         672 :                 if (mb_strchr((char *) ptr->data, c))
     247           0 :                     return false;
     248         672 :                 break;
     249           0 :             default:
     250           0 :                 elog(ERROR, "unrecognized regis node type: %d", ptr->type);
     251             :         }
     252         708 :         ptr = ptr->next;
     253         708 :         c += pg_mblen(c);
     254             :     }
     255             : 
     256         672 :     return true;
     257             : }

Generated by: LCOV version 1.14