Subject: bug#18454: Improve performance when -P (PCRE) is used in UTF-8 locales

From: Paul Eggert <[email-masked]>
Date: Wed, 17 Sep 2014 12:55:50 -0700

Thanks for reporting that, I forgot that the code defaulted SEEK_HOLE 
but not SEEK_DATA.  The first attached patch should fix it.  The second 
one should improve performance further on Solaris for files that end in 
holes.
>From 78497a2aaaaeae439f9546223b45b3b553146f36 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Wed, 17 Sep 2014 12:33:55 -0700
Subject: [PATCH 1/2] grep: port to platforms lacking SEEK_DATA

Reported by Norihiro Tanaka in: http://bugs.gnu.org/18454#38
* src/grep.c (SEEK_DATA): Default to SEEK_SET if not defined.
(SEEK_HOLE): Move to top level, and default it to SEEK_SET.
(file_textbin): Adjust to new default.
(fillbuf): Don't bother with SEEK_DATA if it defaults to SEEK_SET.
---
 src/grep.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/grep.c b/src/grep.c
index 3e94804..a08fa41 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -415,6 +415,15 @@ usable_st_size (struct stat const *st)
   return S_ISREG (st->st_mode) || S_TYPEISSHM (st) || S_TYPEISTMO (st);
 }
 
+/* Lame substitutes for SEEK_DATA and SEEK_HOLE on platforms lacking them.
+   Do not rely on these finding data or holes if they equal SEEK_SET.  */
+#ifndef SEEK_DATA
+enum { SEEK_DATA = SEEK_SET };
+#endif
+#ifndef SEEK_HOLE
+enum { SEEK_HOLE = SEEK_SET };
+#endif
+
 /* Functions we'll use to search. */
 typedef void (*compile_fp_t) (char const *, size_t);
 typedef size_t (*execute_fp_t) (char const *, size_t, size_t *, char const *);
@@ -474,10 +483,6 @@ buffer_textbin (char const *buf, size_t size)
 static enum textbin
 file_textbin (char const *buf, size_t bufsize, int fd, struct stat const *st)
 {
-  #ifndef SEEK_HOLE
-  enum { SEEK_HOLE = SEEK_END };
-  #endif
-
   enum textbin textbin = buffer_textbin (buf, bufsize);
   if (textbin_is_binary (textbin))
     return textbin;
@@ -488,7 +493,7 @@ file_textbin (char const *buf, size_t bufsize, int fd, struct stat const *st)
         return textbin == TEXTBIN_UNKNOWN ? TEXTBIN_BINARY : textbin;
 
       /* If the file has holes, it must contain a null byte somewhere.  */
-      if (SEEK_HOLE != SEEK_END && eolbyte)
+      if (SEEK_HOLE != SEEK_SET && eolbyte)
         {
           off_t cur = bufsize;
           if (O_BINARY || fd == STDIN_FILENO)
@@ -713,7 +718,7 @@ fillbuf (size_t save, struct stat const *st)
         break;
       totalnl = add_count (totalnl, fillsize);
 
-      if (!seek_data_failed)
+      if (SEEK_DATA != SEEK_SET && !seek_data_failed)
         {
           off_t data_start = lseek (bufdesc, bufoffset, SEEK_DATA);
           if (data_start < 0)
-- 
1.9.3

>From 0d6febac38c03391d7eecb5335620a0ec5ba8278 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Wed, 17 Sep 2014 12:53:17 -0700
Subject: [PATCH 2/2] grep: speed up processing of holes before EOF on Solaris

* src/grep.c (fillbuf): If SEEK_DATA fails with errno == ENXIO,
skip over the hole at EOF.
---
 src/grep.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/grep.c b/src/grep.c
index a08fa41..35d3358 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -720,7 +720,12 @@ fillbuf (size_t save, struct stat const *st)
 
       if (SEEK_DATA != SEEK_SET && !seek_data_failed)
         {
+          /* Solaris SEEK_DATA fails with errno == ENXIO in a hole at EOF.  */
           off_t data_start = lseek (bufdesc, bufoffset, SEEK_DATA);
+          if (data_start < 0 && errno == ENXIO
+              && usable_st_size (st) && bufoffset < st->st_size)
+            data_start = lseek (bufdesc, 0, SEEK_END);
+
           if (data_start < 0)
             seek_data_failed = true;
           else
-- 
1.9.3