[gtksourceview/wip/chergert/pcre2] pcre2: start on pcre2 implementation




commit e911ef08dda0270f69ac88838e0996b724a3802e
Author: Christian Hergert <chergert redhat com>
Date:   Fri Sep 25 10:23:18 2020 -0700

    pcre2: start on pcre2 implementation

 gtksourceview/implregex-private.h |   4 +
 gtksourceview/implregex.c         | 414 +++++++++++++++++++++++++++++++-------
 2 files changed, 343 insertions(+), 75 deletions(-)
---
diff --git a/gtksourceview/implregex-private.h b/gtksourceview/implregex-private.h
index da52474e..785d060a 100644
--- a/gtksourceview/implregex-private.h
+++ b/gtksourceview/implregex-private.h
@@ -41,6 +41,7 @@ gboolean    impl_regex_match                (const ImplRegex        *regex,
                                              const char             *string,
                                              GRegexMatchFlags        match_options,
                                              ImplMatchInfo         **match_info);
+ImplRegex  *impl_regex_ref                  (ImplRegex              *regex);
 void        impl_regex_unref                (ImplRegex              *regex);
 void        impl_match_info_free            (ImplMatchInfo          *match_info);
 char       *impl_match_info_fetch           (const ImplMatchInfo    *match_info,
@@ -70,6 +71,9 @@ gboolean    impl_match_info_fetch_named_pos (const ImplMatchInfo    *match_info,
                                              const char             *name,
                                              int                    *start_pos,
                                              int                    *end_pos);
+gboolean    impl_match_info_matches         (const ImplMatchInfo    *match_info);
+gboolean    impl_match_info_next            (ImplMatchInfo          *match_info,
+                                             GError                **error);
 const char *impl_regex_get_pattern          (const ImplRegex        *regex);
 
 G_END_DECLS
diff --git a/gtksourceview/implregex.c b/gtksourceview/implregex.c
index 56a12799..2dee6de8 100644
--- a/gtksourceview/implregex.c
+++ b/gtksourceview/implregex.c
@@ -21,27 +21,49 @@
 
 #include "config.h"
 
+#define PCRE2_CODE_UNIT_WIDTH 8
+
+#include <pcre2.h>
+#include <string.h>
+
 #include "implregex-private.h"
 
+#define IS_PCRE_ERROR(ret) ((ret) < PCRE2_ERROR_NOMATCH && (ret) != PCRE2_ERROR_PARTIAL)
+
 struct _ImplRegex
 {
-       int         ref_count;
-       char       *pattern;
-       GRegex     *re;
+       int                ref_count;
+       char              *pattern;
+       pcre2_code        *code;
+       GRegexMatchFlags   match_options;
+       PCRE2_SPTR         name_table;
+       int                name_count;
+       int                name_entry_size;
 };
 
 struct _ImplMatchInfo
 {
-       GMatchInfo *match_info;
+       ImplRegex        *regex;
+       const char       *string;
+       gsize             string_len;
+       pcre2_match_data *match_data;
+       gsize             flags;
+       int               n_matches;
+       gssize            prev_begin;
+       gssize            prev_end;
 };
 
-#if 0
 static void
 set_regex_error (GError **error,
                  int      errnum)
 {
        guchar errstr[128];
 
+       if (error == NULL)
+       {
+               return;
+       }
+
        pcre2_get_error_message (errnum, errstr, sizeof errstr - 1);
        errstr[sizeof errstr - 1] = 0;
 
@@ -50,15 +72,18 @@ set_regex_error (GError **error,
                             G_REGEX_ERROR_COMPILE,
                             (const gchar *)errstr);
 }
-#endif
 
 static ImplMatchInfo *
-impl_match_info_new (const ImplRegex *regex)
+impl_match_info_new (ImplRegex *regex)
 {
        ImplMatchInfo *match_info;
 
        match_info = g_slice_new0 (ImplMatchInfo);
-       match_info->match_info = NULL;
+       match_info->regex = impl_regex_ref (regex);
+       match_info->match_data = pcre2_match_data_create_from_pattern (regex->code, NULL);
+       match_info->prev_begin = -1;
+       match_info->prev_end = -1;
+       match_info->n_matches = -1;
 
        return match_info;
 }
@@ -69,22 +94,50 @@ impl_regex_new (const char          *pattern,
                 GRegexMatchFlags     match_options,
                 GError             **error)
 {
-       GRegex *re;
+       pcre2_code *code;
        ImplRegex *regex;
+       PCRE2_SIZE erroffset;
+       gsize flags = PCRE2_UTF | PCRE2_NO_UTF_CHECK | PCRE2_ZERO_TERMINATED;
+       int errnumber;
 
        g_return_val_if_fail (pattern != NULL, NULL);
 
-       re = g_regex_new (pattern, compile_options, match_options, error);
+       if (compile_options & G_REGEX_CASELESS)
+               flags |= PCRE2_CASELESS;
+
+       if (compile_options & G_REGEX_NEWLINE_LF)
+               flags |= PCRE2_NEWLINE_LF;
+
+       code = pcre2_compile ((PCRE2_SPTR)pattern,
+                             flags,
+                             0,
+                             &errnumber,
+                             &erroffset,
+                             NULL);
 
-       if (re == NULL)
+       if (code == NULL)
        {
+               set_regex_error (error, errnumber);
                return NULL;
        }
 
        regex = g_slice_new0 (ImplRegex);
        regex->ref_count = 1;
        regex->pattern = g_strdup (pattern);
-       regex->re = re;
+       regex->match_options = match_options;
+       regex->code = code;
+
+       (void)pcre2_pattern_info (code, PCRE2_INFO_NAMECOUNT, &regex->name_count);
+
+       if (regex->name_count > 0)
+       {
+               (void)pcre2_pattern_info (code,
+                                         PCRE2_INFO_NAMEENTRYSIZE,
+                                         &regex->name_entry_size);
+               (void)pcre2_pattern_info (code,
+                                         PCRE2_INFO_NAMETABLE,
+                                         &regex->name_table);
+       }
 
        return regex;
 }
@@ -97,6 +150,17 @@ impl_regex_get_pattern (const ImplRegex *regex)
        return regex->pattern;
 }
 
+ImplRegex *
+impl_regex_ref (ImplRegex *regex)
+{
+       g_return_val_if_fail (regex != NULL, NULL);
+       g_return_val_if_fail (regex->ref_count > 0, NULL);
+
+       regex->ref_count++;
+
+       return regex;
+}
+
 void
 impl_regex_unref (ImplRegex *regex)
 {
@@ -108,7 +172,7 @@ impl_regex_unref (ImplRegex *regex)
        if (regex->ref_count == 0)
        {
                g_clear_pointer (&regex->pattern, g_free);
-               g_clear_pointer (&regex->re, g_regex_unref);
+               g_clear_pointer (&regex->code, pcre2_code_free);
                g_slice_free (ImplRegex, regex);
        }
 }
@@ -116,8 +180,12 @@ impl_regex_unref (ImplRegex *regex)
 void
 impl_match_info_free (ImplMatchInfo *match_info)
 {
-       g_clear_pointer (&match_info->match_info, g_match_info_free);
-       g_slice_free (ImplMatchInfo, match_info);
+       if (match_info != NULL)
+       {
+               g_clear_pointer (&match_info->match_data, pcre2_match_data_free);
+               g_clear_pointer (&match_info->regex, impl_regex_unref);
+               g_slice_free (ImplMatchInfo, match_info);
+       }
 }
 
 gboolean
@@ -126,52 +194,60 @@ impl_regex_match (const ImplRegex   *regex,
                   GRegexMatchFlags   match_options,
                   ImplMatchInfo    **match_info)
 {
+       gboolean ret;
+
        g_return_val_if_fail (regex != NULL, FALSE);
-       g_return_val_if_fail (regex->re != NULL, FALSE);
+       g_return_val_if_fail (regex->code != NULL, FALSE);
 
-       if (match_info != NULL)
-       {
-               *match_info = impl_match_info_new (regex);
-       }
+       ret = impl_regex_match_full (regex,
+                                    string,
+                                    strlen (string),
+                                    0,
+                                    match_options,
+                                    match_info,
+                                    NULL);
 
-       return g_regex_match (regex->re,
-                             string,
-                             match_options,
-                             match_info ? &(*match_info)->match_info : NULL);
+       return ret;
 }
 
 char *
 impl_match_info_fetch (const ImplMatchInfo *match_info,
                        int                  match_num)
 {
+       char *ret;
+       int begin = 0;
+       int end = 0;
+
        g_return_val_if_fail (match_info != NULL, NULL);
 
-       return g_match_info_fetch (match_info->match_info, match_num);
+       if (match_info->string == NULL ||
+           !impl_match_info_fetch_pos (match_info, match_num, &begin, &end) ||
+           begin < 0 ||
+           end < 0)
+               ret = g_strdup ("");
+       else
+               ret = g_strndup (match_info->string + begin, end - begin);
+
+       return ret;
 }
 
 char *
 impl_match_info_fetch_named (const ImplMatchInfo *match_info,
                              const char          *name)
 {
+       char *ret;
+       int begin;
+       int end;
+
        g_return_val_if_fail (match_info != NULL, NULL);
 
-       return g_match_info_fetch_named (match_info->match_info, name);
-}
+       if (match_info->string == NULL ||
+           !impl_match_info_fetch_named_pos (match_info, name, &begin, &end))
+               ret = g_strdup ("");
+       else
+               ret = g_strndup (match_info->string + begin, end - begin);
 
-static gboolean
-wrapper_eval (const GMatchInfo *match_info,
-              GString          *result,
-              gpointer          user_data)
-{
-       struct {
-               ImplRegexEvalCallback callback;
-               gpointer user_data;
-       } *wrapper = user_data;
-       ImplMatchInfo wrapped = {
-               .match_info = (GMatchInfo *)match_info,
-       };
-
-       return wrapper->callback (&wrapped, result, wrapper->user_data);
+       return ret;
 }
 
 char *
@@ -184,25 +260,70 @@ impl_regex_replace_eval (const ImplRegex        *regex,
                          gpointer                user_data,
                          GError                **error)
 {
-       struct {
-               ImplRegexEvalCallback callback;
-               gpointer user_data;
-       } wrapper;
+       ImplMatchInfo *match_info;
+       GString *out_string;
+       gboolean done;
+       int str_pos;
 
        g_return_val_if_fail (regex != NULL, NULL);
-       g_return_val_if_fail (regex->re != NULL, NULL);
+       g_return_val_if_fail (regex->code != NULL, NULL);
 
-       wrapper.callback = eval;
-       wrapper.user_data = user_data;
+       if (string_len < 0)
+       {
+               string_len = strlen (string);
+       }
 
-       return g_regex_replace_eval (regex->re,
-                                    string,
-                                    string_len,
-                                    start_position,
-                                    match_options,
-                                    wrapper_eval,
-                                    &wrapper,
-                                    error);
+       if (start_position < 0)
+       {
+               start_position = 0;
+       }
+
+       match_info = NULL;
+
+       if (!impl_regex_match_full (regex,
+                                   string,
+                                   string_len,
+                                   start_position,
+                                   match_options,
+                                   &match_info,
+                                   error))
+       {
+               impl_match_info_free (match_info);
+               return g_strndup (string, string_len);
+       }
+
+       g_assert (match_info != NULL);
+       g_assert (match_info->n_matches > 0);
+
+       str_pos = 0;
+       out_string = g_string_sized_new (string_len);
+       done = FALSE;
+
+       while (!done && impl_match_info_matches (match_info))
+       {
+               g_assert (match_info->prev_begin >= 0);
+               g_assert (match_info->prev_end >= 0);
+
+               g_string_append_len (out_string,
+                                    string + str_pos,
+                                    match_info->prev_begin - str_pos);
+               str_pos = match_info->prev_end;
+
+               done = eval (match_info, out_string, user_data);
+
+               if (!impl_match_info_next (match_info, NULL))
+               {
+                       break;
+               }
+       }
+
+       g_string_append_len (out_string,
+                            string + str_pos,
+                            string_len - str_pos);
+
+       impl_match_info_free (match_info);
+
+       return g_string_free (out_string, FALSE);
 }
 
 gboolean
@@ -214,28 +335,43 @@ impl_regex_match_full (const ImplRegex   *regex,
                        ImplMatchInfo    **match_info,
                        GError           **error)
 {
-       GMatchInfo *wrapped = NULL;
-       gboolean ret;
+       ImplMatchInfo *fallback = NULL;
+       gsize flags = PCRE2_NO_UTF_CHECK;
+       gboolean ret = FALSE;
 
        g_return_val_if_fail (regex != NULL, FALSE);
-       g_return_val_if_fail (regex->re != NULL, FALSE);
+       g_return_val_if_fail (regex->code != NULL, FALSE);
+       g_return_val_if_fail (start_position >= 0, FALSE);
+       g_return_val_if_fail (match_options == 0, FALSE);
 
-       ret = g_regex_match_full (regex->re,
-                                 string,
-                                 string_len,
-                                 start_position,
-                                 match_options,
-                                 &wrapped,
-                                 error);
+       if (match_info == NULL)
+       {
+               match_info = &fallback;
+       }
 
-       if (match_info != NULL)
+       if (string_len < 0)
        {
-               *match_info = g_slice_new0 (ImplMatchInfo);
-               (*match_info)->match_info = wrapped;
+               string_len = strlen (string);
        }
-       else
+
+       *match_info = impl_match_info_new ((ImplRegex *)regex);
+
+       if ((match_options | regex->match_options) & G_REGEX_ANCHORED)
+       {
+               flags |= PCRE2_ANCHORED;
+       }
+
+       (*match_info)->flags = flags;
+       (*match_info)->string = string;
+       (*match_info)->string_len = string_len;
+       (*match_info)->prev_begin = start_position;
+       (*match_info)->prev_end = start_position;
+
+       ret = impl_match_info_next (*match_info, error);
+
+       if (fallback != NULL)
        {
-               g_match_info_free (wrapped);
+               impl_match_info_free (fallback);
        }
 
        return ret;
@@ -247,10 +383,31 @@ impl_match_info_fetch_pos (const ImplMatchInfo *match_info,
                            int                 *start_pos,
                            int                 *end_pos)
 {
+       PCRE2_SIZE *ovector;
+       int real_start_pos;
+       int real_end_pos;
+
        g_return_val_if_fail (match_info != NULL, FALSE);
-       g_return_val_if_fail (match_info->match_info != NULL, FALSE);
+       g_return_val_if_fail (match_info->match_data != NULL, FALSE);
+       g_return_val_if_fail (match_num >= 0, FALSE);
+
+       if (match_num >= match_info->n_matches)
+       {
+               return FALSE;
+       }
+
+       ovector = pcre2_get_ovector_pointer (match_info->match_data);
+
+       real_start_pos = ovector[2*match_num];
+       real_end_pos = ovector[2*match_num+1];
 
-       return g_match_info_fetch_pos (match_info->match_info, match_num, start_pos, end_pos);
+       if (start_pos != NULL)
+               *start_pos = real_start_pos;
+
+       if (end_pos != NULL)
+               *end_pos = real_end_pos;
+
+       return TRUE;
 }
 
 gboolean
@@ -258,9 +415,116 @@ impl_match_info_fetch_named_pos (const ImplMatchInfo *match_info,
                                  const char          *name,
                                  int                 *start_pos,
                                  int                 *end_pos)
+{
+       PCRE2_SPTR tabptr;
+
+       g_return_val_if_fail (match_info != NULL, FALSE);
+       g_return_val_if_fail (match_info->match_data != NULL, FALSE);
+       g_return_val_if_fail (match_info->regex != NULL, FALSE);
+       g_return_val_if_fail (start_pos != NULL, FALSE);
+       g_return_val_if_fail (end_pos != NULL, FALSE);
+
+       tabptr = match_info->regex->name_table;
+
+       for (int i = 0; i < match_info->regex->name_count; i++)
+       {
+               int n = (tabptr[0] << 8) | tabptr[1];
+
+               if (g_strcmp0 (name, (const char *)(tabptr+2)) == 0)
+               {
+                       return impl_match_info_fetch_pos (match_info, n, start_pos, end_pos);
+               }
+
+               tabptr += match_info->regex->name_entry_size;
+       }
+
+       *start_pos = -1;
+       *end_pos = -1;
+
+       return FALSE;
+}
+
+gboolean
+impl_match_info_matches (const ImplMatchInfo *match_info)
 {
        g_return_val_if_fail (match_info != NULL, FALSE);
-       g_return_val_if_fail (match_info->match_info != NULL, FALSE);
 
-       return g_match_info_fetch_named_pos (match_info->match_info, name, start_pos, end_pos);
+       return match_info->n_matches > 0;
+}
+
+gboolean
+impl_match_info_next (ImplMatchInfo  *match_info,
+                      GError        **error)
+{
+       PCRE2_SIZE *ovector;
+       gssize prev_begin;
+       gssize prev_end;
+       int rc;
+
+       g_return_val_if_fail (match_info != NULL, FALSE);
+       g_return_val_if_fail (match_info->regex != NULL, FALSE);
+
+       prev_begin = match_info->prev_begin;
+       prev_end = match_info->prev_end;
+
+       if (prev_end > match_info->string_len)
+       {
+               goto nomatch;
+       }
+
+       rc = pcre2_match (match_info->regex->code,
+                         (PCRE2_SPTR)match_info->string,
+                         (PCRE2_SIZE)match_info->string_len,
+                         prev_end,
+                         match_info->flags,
+                         match_info->match_data,
+                         NULL);
+
+       if (IS_PCRE_ERROR (rc))
+       {
+               set_regex_error (error, rc);
+               return FALSE;
+       }
+
+       ovector = pcre2_get_ovector_pointer (match_info->match_data);
+
+       if (match_info->prev_end == ovector[1])
+       {
+               const char *next = g_utf8_next_char (match_info->string + prev_end);
+
+               match_info->prev_end = next - match_info->string;
+               match_info->prev_begin = match_info->prev_end;
+       }
+       else
+       {
+               match_info->prev_begin = ovector[0];
+               match_info->prev_end = ovector[1];
+       }
+
+       if (rc > 0)
+       {
+               match_info->n_matches = rc;
+
+               /* see bug #515944: http://bugzilla.gnome.org/show_bug.cgi?id=515944 */
+               if (prev_begin == match_info->prev_begin &&
+                   prev_end == match_info->prev_end)
+               {
+                       /* ignore this match and search the next one */
+                       return impl_match_info_next (match_info, error);
+               }
+       }
+       else
+       {
+               goto nomatch;
+       }
+
+       return TRUE;
+
+nomatch:
+       match_info->n_matches = PCRE2_ERROR_NOMATCH;
+       match_info->prev_begin = -1;
+       match_info->prev_end = -1;
+       set_regex_error (error, PCRE2_ERROR_NOMATCH);
+
+       return FALSE;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]