diff --git a/7.3.427 b/7.3.427 new file mode 100644 index 0000000..cc77ac3 --- /dev/null +++ b/7.3.427 @@ -0,0 +1,395 @@ +To: vim_dev@googlegroups.com +Subject: Patch 7.3.427 +Fcc: outbox +From: Bram Moolenaar +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +------------ + +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 -- Bram@Moolenaar.net -- 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 ///