Patch 7.3.427
Problem:    readfile() can be slow with long lines.
Solution:   Use realloc() instead of alloc(). (John Little)
Files:      src/eval.c


*** ../vim-7.3.426/src/eval.c   2012-01-26 14:32:26.000000000 +0100
--- src/eval.c  2012-02-05 00:25:39.000000000 +0100
***************
*** 14325,14346 ****
      typval_T  *rettv;
  {
      int               binary = FALSE;
      char_u    *fname;
      FILE      *fd;
!     listitem_T        *li;
! #define FREAD_SIZE 200            /* optimized for text lines */
!     char_u    buf[FREAD_SIZE];
!     int               readlen;    /* size of last fread() */
!     int               buflen;     /* nr of valid chars in buf[] */
!     int               filtd;      /* how much in buf[] was NUL -> '\n' 
filtered */
!     int               tolist;     /* first byte in buf[] still to be put in 
list */
!     int               chop;       /* how many CR to chop off */
!     char_u    *prev = NULL;   /* previously read bytes, if any */
!     int               prevlen = 0;    /* length of "prev" if not NULL */
!     char_u    *s;
!     int               len;
!     long      maxline = MAXLNUM;
!     long      cnt = 0;
  
      if (argvars[1].v_type != VAR_UNKNOWN)
      {
--- 14325,14343 ----
      typval_T  *rettv;
  {
      int               binary = FALSE;
+     int               failed = FALSE;
      char_u    *fname;
      FILE      *fd;
!     char_u    buf[(IOSIZE/256)*256];  /* rounded to avoid odd + 1 */
!     int               io_size = sizeof(buf);
!     int               readlen;                /* size of last fread() */
!     char_u    *prev    = NULL;        /* previously read bytes, if any */
!     long      prevlen  = 0;           /* length of data in prev */
!     long      prevsize = 0;           /* size of prev buffer */
!     long      maxline  = MAXLNUM;
!     long      cnt      = 0;
!     char_u    *p;                     /* position in buf */
!     char_u    *start;                 /* start of current line */
  
      if (argvars[1].v_type != VAR_UNKNOWN)
      {
***************
*** 14362,14410 ****
        return;
      }
  
-     filtd = 0;
      while (cnt < maxline || maxline < 0)
      {
!       readlen = (int)fread(buf + filtd, 1, FREAD_SIZE - filtd, fd);
!       buflen = filtd + readlen;
!       tolist = 0;
!       for ( ; filtd < buflen || readlen <= 0; ++filtd)
!       {
!           if (readlen <= 0 || buf[filtd] == '\n')
!           {
!               /* In binary mode add an empty list item when the last
!                * non-empty line ends in a '\n'. */
!               if (!binary && readlen == 0 && filtd == 0 && prev == NULL)
!                   break;
  
!               /* Found end-of-line or end-of-file: add a text line to the
!                * list. */
!               chop = 0;
!               if (!binary)
!                   while (filtd - chop - 1 >= tolist
!                                         && buf[filtd - chop - 1] == '\r')
!                       ++chop;
!               len = filtd - tolist - chop;
!               if (prev == NULL)
!                   s = vim_strnsave(buf + tolist, len);
                else
                {
!                   s = alloc((unsigned)(prevlen + len + 1));
!                   if (s != NULL)
                    {
!                       mch_memmove(s, prev, prevlen);
!                       vim_free(prev);
!                       prev = NULL;
!                       mch_memmove(s + prevlen, buf + tolist, len);
                        s[prevlen + len] = NUL;
                    }
                }
!               tolist = filtd + 1;
  
!               li = listitem_alloc();
!               if (li == NULL)
                {
                    vim_free(s);
                    break;
                }
                li->li_tv.v_type = VAR_STRING;
--- 14359,14419 ----
        return;
      }
  
      while (cnt < maxline || maxline < 0)
      {
!       readlen = (int)fread(buf, 1, io_size, fd);
! 
!       /* This for loop processes what was read, but is also entered at end
!        * of file so that either:
!        * - an incomplete line gets written
!        * - a "binary" file gets an empty line at the end if it ends in a
!        *   newline.  */
!       for (p = buf, start = buf;
!               p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary));
!               ++p)
!       {
!           if (*p == '\n' || readlen <= 0)
!           {
!               listitem_T  *li;
!               char_u      *s  = NULL;
!               long_u      len = p - start;
  
!               /* Finished a line.  Remove CRs before NL. */
!               if (readlen > 0 && !binary)
!               {
!                   while (len > 0 && start[len - 1] == '\r')
!                       --len;
!                   /* removal may cross back to the "prev" string */
!                   if (len == 0)
!                       while (prevlen > 0 && prev[prevlen - 1] == '\r')
!                           --prevlen;
!               }
!               if (prevlen == 0)
!                   s = vim_strnsave(start, len);
                else
                {
!                   /* Change "prev" buffer to be the right size.  This way
!                    * the bytes are only copied once, and very long lines are
!                    * allocated only once.  */
!                   if ((s = vim_realloc(prev, prevlen + len + 1)) != NULL)
                    {
!                       mch_memmove(s + prevlen, start, len);
                        s[prevlen + len] = NUL;
+                       prev = NULL; /* the list will own the string */
+                       prevlen = prevsize = 0;
                    }
                }
!               if (s == NULL)
!               {
!                   do_outofmem_msg((long_u) prevlen + len + 1);
!                   failed = TRUE;
!                   break;
!               }
  
!               if ((li = listitem_alloc()) == NULL)
                {
                    vim_free(s);
+                   failed = TRUE;
                    break;
                }
                li->li_tv.v_type = VAR_STRING;
***************
*** 14412,14485 ****
                li->li_tv.vval.v_string = s;
                list_append(rettv->vval.v_list, li);
  
!               if (++cnt >= maxline && maxline >= 0)
!                   break;
!               if (readlen <= 0)
                    break;
            }
!           else if (buf[filtd] == NUL)
!               buf[filtd] = '\n';
  #ifdef FEAT_MBYTE
!           else if (buf[filtd] == 0xef
!                   && enc_utf8
!                   && filtd + 2 < buflen
!                   && !binary
!                   && buf[filtd + 1] == 0xbb
!                   && buf[filtd + 2] == 0xbf)
!           {
!               /* remove utf-8 byte order mark */
!               mch_memmove(buf + filtd, buf + filtd + 3, buflen - filtd - 3);
!               --filtd;
!               buflen -= 3;
            }
  #endif
!       }
!       if (readlen <= 0)
!           break;
  
!       if (tolist == 0)
        {
!           if (buflen >= FREAD_SIZE / 2)
            {
!               /* "buf" is full, need to move text to an allocated buffer */
!               if (prev == NULL)
                {
!                   prev = vim_strnsave(buf, buflen);
!                   prevlen = buflen;
                }
!               else
                {
!                   s = alloc((unsigned)(prevlen + buflen));
!                   if (s != NULL)
!                   {
!                       mch_memmove(s, prev, prevlen);
!                       mch_memmove(s + prevlen, buf, buflen);
!                       vim_free(prev);
!                       prev = s;
!                       prevlen += buflen;
!                   }
                }
!               filtd = 0;
            }
        }
!       else
!       {
!           mch_memmove(buf, buf + tolist, buflen - tolist);
!           filtd -= tolist;
!       }
!     }
  
      /*
       * For a negative line count use only the lines at the end of the file,
       * free the rest.
       */
!     if (maxline < 0)
        while (cnt > -maxline)
        {
            listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
            --cnt;
        }
  
      vim_free(prev);
      fclose(fd);
  }
--- 14421,14529 ----
                li->li_tv.vval.v_string = s;
                list_append(rettv->vval.v_list, li);
  
!               start = p + 1; /* step over newline */
!               if ((++cnt >= maxline && maxline >= 0) || readlen <= 0)
                    break;
            }
!           else if (*p == NUL)
!               *p = '\n';
  #ifdef FEAT_MBYTE
!           /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF.  Do this
!            * when finding the BF and check the previous two bytes. */
!           else if (*p == 0xbf && enc_utf8 && !binary)
!           {
!               /* Find the two bytes before the 0xbf.  If p is at buf, or buf
!                * + 1, these may be in the "prev" string. */
!               char_u back1 = p >= buf + 1 ? p[-1]
!                                    : prevlen >= 1 ? prev[prevlen - 1] : NUL;
!               char_u back2 = p >= buf + 2 ? p[-2]
!                         : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1]
!                         : prevlen >= 2 ? prev[prevlen - 2] : NUL;
! 
!               if (back2 == 0xef && back1 == 0xbb)
!               {
!                   char_u *dest = p - 2;
! 
!                   /* Usually a BOM is at the beginning of a file, and so at
!                    * the beginning of a line; then we can just step over it.
!                    */
!                   if (start == dest)
!                       start = p + 1;
!                   else
!                   {
!                       /* have to shuffle buf to close gap */
!                       int adjust_prevlen = 0;
! 
!                       if (dest < buf)
!                       {
!                           adjust_prevlen = buf - dest; /* must be 1 or 2 */
!                           dest = buf;
!                       }
!                       if (readlen > p - buf + 1)
!                           mch_memmove(dest, p + 1, readlen - (p - buf) - 1);
!                       readlen -= 3 - adjust_prevlen;
!                       prevlen -= adjust_prevlen;
!                       p = dest - 1;
!                   }
!               }
            }
  #endif
!       } /* for */
  
!       if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0)
!           break;
!       if (start < p)
        {
!           /* There's part of a line in buf, store it in "prev". */
!           if (p - start + prevlen >= prevsize)
            {
!               /* need bigger "prev" buffer */
!               char_u *newprev;
! 
!               /* A common use case is ordinary text files and "prev" gets a
!                * fragment of a line, so the first allocation is made
!                * small, to avoid repeatedly 'allocing' large and
!                * 'reallocing' small. */
!               if (prevsize == 0)
!                   prevsize = p - start;
!               else
                {
!                   long grow50pc = (prevsize * 3) / 2;
!                   long growmin  = (p - start) * 2 + prevlen;
!                   prevsize = grow50pc > growmin ? grow50pc : growmin;
                }
!               if ((newprev = vim_realloc(prev, prevsize)) == NULL)
                {
!                   do_outofmem_msg((long_u)prevsize);
!                   failed = TRUE;
!                   break;
                }
!               prev = newprev;
            }
+           /* Add the line part to end of "prev". */
+           mch_memmove(prev + prevlen, start, p - start);
+           prevlen += p - start;
        }
!     } /* while */
  
      /*
       * For a negative line count use only the lines at the end of the file,
       * free the rest.
       */
!     if (!failed && maxline < 0)
        while (cnt > -maxline)
        {
            listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
            --cnt;
        }
  
+     if (failed)
+     {
+       list_free(rettv->vval.v_list, TRUE);
+       /* readfile doc says an empty list is returned on error */
+       rettv->vval.v_list = list_alloc();
+     }
+ 
      vim_free(prev);
      fclose(fd);
  }
***************
*** 21802,21808 ****
        fp = HI2UF(hi);
  
        if (fp->uf_flags & FC_DICT)
!           return ""; /* don't show dict functions */
  
        if (STRLEN(fp->uf_name) + 4 >= IOSIZE)
            return fp->uf_name; /* prevents overflow */
--- 21846,21852 ----
        fp = HI2UF(hi);
  
        if (fp->uf_flags & FC_DICT)
!           return (char_u *)""; /* don't show dict functions */
  
        if (STRLEN(fp->uf_name) + 4 >= IOSIZE)
            return fp->uf_name; /* prevents overflow */
*** ../vim-7.3.426/src/version.c        2012-02-04 23:34:57.000000000 +0100
--- src/version.c       2012-02-05 00:38:34.000000000 +0100
***************
*** 716,717 ****
--- 716,719 ----
  {   /* Add new patch number below this line */
+ /**/
+     427,
  /**/

-- 
One difference between a man and a machine is that a machine is quiet
when well oiled.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

Raspunde prin e-mail lui