Inline functions to examine and change buffer intervals.
[bpt/emacs.git] / src / bidi.c
index 564d985..4df585d 100644 (file)
@@ -1,5 +1,5 @@
 /* Low-level bidirectional buffer/string-scanning functions for GNU Emacs.
-   Copyright (C) 2000-2001, 2004-2005, 2009-2011
+   Copyright (C) 2000-2001, 2004-2005, 2009-2012
    Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -51,7 +51,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
    A note about references to UAX#9 rules: if the reference says
    something like "X9/Retaining", it means that you need to refer to
-   rule X9 and to its modifications decribed in the "Implementation
+   rule X9 and to its modifications described in the "Implementation
    Notes" section of UAX#9, under "Retaining Format Codes".  */
 
 #include <config.h>
@@ -59,8 +59,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <setjmp.h>
 
 #include "lisp.h"
-#include "buffer.h"
 #include "character.h"
+#include "buffer.h"
 #include "dispextern.h"
 
 static int bidi_initialized = 0;
@@ -149,7 +149,7 @@ bidi_get_type (int ch, bidi_dir_t override)
 static inline void
 bidi_check_type (bidi_type_t type)
 {
-  xassert (UNKNOWN_BT <= type && type <= NEUTRAL_ON);
+  eassert (UNKNOWN_BT <= type && type <= NEUTRAL_ON);
 }
 
 /* Given a bidi TYPE of a character, return its category.  */
@@ -204,8 +204,16 @@ bidi_mirror_char (int c)
   val = CHAR_TABLE_REF (bidi_mirror_table, c);
   if (INTEGERP (val))
     {
-      int v = XINT (val);
+      int v;
+
+      /* When debugging, check before assigning to V, so that the check
+        isn't broken by undefined behavior due to int overflow.  */
+      eassert (CHAR_VALID_P (XINT (val)));
 
+      v = XINT (val);
+
+      /* Minimal test we must do in optimized builds, to prevent weird
+        crashes further down the road.  */
       if (v < 0 || v > MAX_CHAR)
        abort ();
 
@@ -222,7 +230,7 @@ bidi_mirror_char (int c)
 static inline void
 bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
 {
-  int higher_level = level_before > level_after ? level_before : level_after;
+  int higher_level = (level_before > level_after ? level_before : level_after);
 
   /* The prev_was_pdf gork is required for when we have several PDFs
      in a row.  In that case, we want to compute the sor type for the
@@ -233,18 +241,18 @@ bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
      level to which we descend after processing all the PDFs.  */
   if (!bidi_it->prev_was_pdf || level_before < level_after)
     /* FIXME: should the default sor direction be user selectable?  */
-    bidi_it->sor = (higher_level & 1) != 0 ? R2L : L2R;
+    bidi_it->sor = ((higher_level & 1) != 0 ? R2L : L2R);
   if (level_before > level_after)
     bidi_it->prev_was_pdf = 1;
 
   bidi_it->prev.type = UNKNOWN_BT;
-  bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
-    bidi_it->last_strong.orig_type = UNKNOWN_BT;
-  bidi_it->prev_for_neutral.type = bidi_it->sor == R2L ? STRONG_R : STRONG_L;
+  bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1
+    bidi_it->last_strong.orig_type = UNKNOWN_BT;
+  bidi_it->prev_for_neutral.type = (bidi_it->sor == R2L ? STRONG_R : STRONG_L);
   bidi_it->prev_for_neutral.charpos = bidi_it->charpos;
   bidi_it->prev_for_neutral.bytepos = bidi_it->bytepos;
-  bidi_it->next_for_neutral.type = bidi_it->next_for_neutral.type_after_w1 =
-    bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
+  bidi_it->next_for_neutral.type = bidi_it->next_for_neutral.type_after_w1
+    bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
   bidi_it->ignore_bn_limit = -1; /* meaning it's unknown */
 }
 
@@ -255,7 +263,7 @@ bidi_push_embedding_level (struct bidi_it *bidi_it,
                           int level, bidi_dir_t override)
 {
   bidi_it->stack_idx++;
-  xassert (bidi_it->stack_idx < BIDI_MAXLEVEL);
+  eassert (bidi_it->stack_idx < BIDI_MAXLEVEL);
   bidi_it->level_stack[bidi_it->stack_idx].level = level;
   bidi_it->level_stack[bidi_it->stack_idx].override = override;
 }
@@ -325,10 +333,10 @@ static int bidi_cache_sp;
 /* Size of header used by bidi_shelve_cache.  */
 enum
   {
-    bidi_shelve_header_size =
-      (sizeof (bidi_cache_idx) + sizeof (bidi_cache_start_stack)
-       + sizeof (bidi_cache_sp) + sizeof (bidi_cache_start)
-       + sizeof (bidi_cache_last_idx))
+    bidi_shelve_header_size
+      (sizeof (bidi_cache_idx) + sizeof (bidi_cache_start_stack)
+        + sizeof (bidi_cache_sp) + sizeof (bidi_cache_start)
+        + sizeof (bidi_cache_last_idx))
   };
 
 /* Reset the cache state to the empty state.  We only reset the part
@@ -353,8 +361,7 @@ bidi_cache_shrink (void)
 {
   if (bidi_cache_size > BIDI_CACHE_CHUNK)
     {
-      bidi_cache =
-       (struct bidi_it *) xrealloc (bidi_cache, BIDI_CACHE_CHUNK * elsz);
+      bidi_cache = xrealloc (bidi_cache, BIDI_CACHE_CHUNK * elsz);
       bidi_cache_size = BIDI_CACHE_CHUNK;
     }
   bidi_cache_reset ();
@@ -378,7 +385,7 @@ bidi_cache_fetch_state (ptrdiff_t idx, struct bidi_it *bidi_it)
    resolved levels in cached states.  DIR, if non-zero, means search
    in that direction from the last cache hit.  */
 static inline ptrdiff_t
-bidi_cache_search (EMACS_INT charpos, int level, int dir)
+bidi_cache_search (ptrdiff_t charpos, int level, int dir)
 {
   ptrdiff_t i, i_start;
 
@@ -450,7 +457,7 @@ bidi_cache_find_level_change (int level, int dir, int before)
       ptrdiff_t i = dir ? bidi_cache_last_idx : bidi_cache_idx - 1;
       int incr = before ? 1 : 0;
 
-      xassert (!dir || bidi_cache_last_idx >= 0);
+      eassert (!dir || bidi_cache_last_idx >= 0);
 
       if (!dir)
        dir = -1;
@@ -490,17 +497,17 @@ bidi_cache_ensure_space (ptrdiff_t idx)
     {
       /* The bidi cache cannot be larger than the largest Lisp string
         or buffer.  */
-      ptrdiff_t string_or_buffer_bound =
-       max (BUF_BYTES_MAX, STRING_BYTES_BOUND);
+      ptrdiff_t string_or_buffer_bound
+       max (BUF_BYTES_MAX, STRING_BYTES_BOUND);
 
       /* Also, it cannot be larger than what C can represent.  */
-      ptrdiff_t c_bound =
-       (min (PTRDIFF_MAX, SIZE_MAX) - bidi_shelve_header_size) / elsz;
+      ptrdiff_t c_bound
+       (min (PTRDIFF_MAX, SIZE_MAX) - bidi_shelve_header_size) / elsz;
 
-      bidi_cache =
-       xpalloc (bidi_cache, &bidi_cache_size,
-                max (BIDI_CACHE_CHUNK, idx - bidi_cache_size + 1),
-                min (string_or_buffer_bound, c_bound), elsz);
+      bidi_cache
+       xpalloc (bidi_cache, &bidi_cache_size,
+                  max (BIDI_CACHE_CHUNK, idx - bidi_cache_size + 1),
+                  min (string_or_buffer_bound, c_bound), elsz);
     }
 }
 
@@ -562,7 +569,7 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
 }
 
 static inline bidi_type_t
-bidi_cache_find (EMACS_INT charpos, int level, struct bidi_it *bidi_it)
+bidi_cache_find (ptrdiff_t charpos, int level, struct bidi_it *bidi_it)
 {
   ptrdiff_t i = bidi_cache_search (charpos, level, bidi_it->scan_dir);
 
@@ -572,7 +579,7 @@ bidi_cache_find (EMACS_INT charpos, int level, struct bidi_it *bidi_it)
 
       bidi_copy_it (bidi_it, &bidi_cache[i]);
       bidi_cache_last_idx = i;
-      /* Don't let scan direction from from the cached state override
+      /* Don't let scan direction from the cached state override
         the current scan direction.  */
       bidi_it->scan_dir = current_scan_dir;
       return bidi_it->type;
@@ -608,7 +615,7 @@ bidi_push_it (struct bidi_it *bidi_it)
   memcpy (&bidi_cache[bidi_cache_idx++], bidi_it, sizeof (struct bidi_it));
 
   /* Push the current cache start onto the stack.  */
-  xassert (bidi_cache_sp < IT_STACK_SIZE);
+  eassert (bidi_cache_sp < IT_STACK_SIZE);
   bidi_cache_start_stack[bidi_cache_sp++] = bidi_cache_start;
 
   /* Start a new level of cache, and make it empty.  */
@@ -708,8 +715,8 @@ bidi_unshelve_cache (void *databuf, int just_free)
          ptrdiff_t idx;
 
          memcpy (&idx, p, sizeof (bidi_cache_idx));
-         bidi_cache_total_alloc -=
-           bidi_shelve_header_size + idx * sizeof (struct bidi_it);
+         bidi_cache_total_alloc
+           -= bidi_shelve_header_size + idx * sizeof (struct bidi_it);
        }
       else
        {
@@ -737,8 +744,9 @@ bidi_unshelve_cache (void *databuf, int just_free)
                  + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
                  + sizeof (bidi_cache_start),
                  sizeof (bidi_cache_last_idx));
-         bidi_cache_total_alloc -=
-           bidi_shelve_header_size + bidi_cache_idx * sizeof (struct bidi_it);
+         bidi_cache_total_alloc
+           -= (bidi_shelve_header_size
+               + bidi_cache_idx * sizeof (struct bidi_it));
        }
 
       xfree (p);
@@ -794,7 +802,7 @@ bidi_set_paragraph_end (struct bidi_it *bidi_it)
 
 /* Initialize the bidi iterator from buffer/string position CHARPOS.  */
 void
-bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p,
+bidi_init_it (ptrdiff_t charpos, ptrdiff_t bytepos, int frame_window_p,
              struct bidi_it *bidi_it)
 {
   if (! bidi_initialized)
@@ -813,18 +821,18 @@ bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p,
   bidi_it->type_after_w1 = NEUTRAL_B;
   bidi_it->orig_type = NEUTRAL_B;
   bidi_it->prev_was_pdf = 0;
-  bidi_it->prev.type = bidi_it->prev.type_after_w1 =
-    bidi_it->prev.orig_type = UNKNOWN_BT;
-  bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
-    bidi_it->last_strong.orig_type = UNKNOWN_BT;
+  bidi_it->prev.type = bidi_it->prev.type_after_w1
+    bidi_it->prev.orig_type = UNKNOWN_BT;
+  bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1
+    bidi_it->last_strong.orig_type = UNKNOWN_BT;
   bidi_it->next_for_neutral.charpos = -1;
-  bidi_it->next_for_neutral.type =
-    bidi_it->next_for_neutral.type_after_w1 =
-    bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
+  bidi_it->next_for_neutral.type
+    = bidi_it->next_for_neutral.type_after_w1
+    bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
   bidi_it->prev_for_neutral.charpos = -1;
-  bidi_it->prev_for_neutral.type =
-    bidi_it->prev_for_neutral.type_after_w1 =
-    bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
+  bidi_it->prev_for_neutral.type
+    = bidi_it->prev_for_neutral.type_after_w1
+    bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
   bidi_it->sor = L2R;   /* FIXME: should it be user-selectable? */
   bidi_it->disp_pos = -1;      /* invalid/unknown */
   bidi_it->disp_prop = 0;
@@ -845,10 +853,13 @@ bidi_line_init (struct bidi_it *bidi_it)
   bidi_it->level_stack[0].override = NEUTRAL_DIR; /* X1 */
   bidi_it->invalid_levels = 0;
   bidi_it->invalid_rl_levels = -1;
-  bidi_it->next_en_pos = -1;
+  /* Setting this to zero will force its recomputation the first time
+     we need it for W5.  */
+  bidi_it->next_en_pos = 0;
+  bidi_it->next_en_type = UNKNOWN_BT;
   bidi_it->next_for_ws.type = UNKNOWN_BT;
   bidi_set_sor_type (bidi_it,
-                    bidi_it->paragraph_dir == R2L ? 1 : 0,
+                    (bidi_it->paragraph_dir == R2L ? 1 : 0),
                     bidi_it->level_stack[0].level); /* X10 */
 
   bidi_cache_reset ();
@@ -863,11 +874,11 @@ bidi_line_init (struct bidi_it *bidi_it)
    are zero-based character positions in S, BEGBYTE is byte position
    corresponding to BEG.  UNIBYTE, if non-zero, means S is a unibyte
    string.  */
-static inline EMACS_INT
-bidi_count_bytes (const unsigned char *s, const EMACS_INT beg,
-                 const EMACS_INT begbyte, const EMACS_INT end, int unibyte)
+static inline ptrdiff_t
+bidi_count_bytes (const unsigned char *s, const ptrdiff_t beg,
+                 const ptrdiff_t begbyte, const ptrdiff_t end, int unibyte)
 {
-  EMACS_INT pos = beg;
+  ptrdiff_t pos = beg;
   const unsigned char *p = s + begbyte, *start = p;
 
   if (unibyte)
@@ -892,7 +903,7 @@ bidi_count_bytes (const unsigned char *s, const EMACS_INT beg,
    character from the current buffer.  UNIBYTE non-zero means S is a
    unibyte string.  */
 static inline int
-bidi_char_at_pos (EMACS_INT bytepos, const unsigned char *s, int unibyte)
+bidi_char_at_pos (ptrdiff_t bytepos, const unsigned char *s, int unibyte)
 {
   if (s)
     {
@@ -910,25 +921,25 @@ bidi_char_at_pos (EMACS_INT bytepos, const unsigned char *s, int unibyte)
    covered characters as a single character, either u+2029 or u+FFFC,
    and return their combined length in CH_LEN and NCHARS.  DISP_POS
    specifies the character position of the next display string, or -1
-   if not yet computed.  DISP_PROP non-zero means that there's really
+   if not yet computed.  When the next character is at or beyond that
+   position, the function updates DISP_POS with the position of the
+   next display string.  DISP_PROP non-zero means that there's really
    a display string at DISP_POS, as opposed to when we searched till
    DISP_POS without finding one.  If DISP_PROP is 2, it means the
    display spec is of the form `(space ...)', which is replaced with
-   u+2029 to handle it as a paragraph separator.  When the next
-   character is at or beyond that position, the function updates
-   DISP_POS with the position of the next display string.  STRING->s
-   is the C string to iterate, or NULL if iterating over a buffer or a
-   Lisp string; in the latter case, STRING->lstring is the Lisp
-   string.  */
+   u+2029 to handle it as a paragraph separator.  STRING->s is the C
+   string to iterate, or NULL if iterating over a buffer or a Lisp
+   string; in the latter case, STRING->lstring is the Lisp string.  */
 static inline int
-bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
+bidi_fetch_char (ptrdiff_t bytepos, ptrdiff_t charpos, ptrdiff_t *disp_pos,
                 int *disp_prop, struct bidi_string_data *string,
-                int frame_window_p, EMACS_INT *ch_len, EMACS_INT *nchars)
+                int frame_window_p, ptrdiff_t *ch_len, ptrdiff_t *nchars)
 {
   int ch;
-  EMACS_INT endpos =
-    (string->s || STRINGP (string->lstring)) ? string->schars : ZV;
+  ptrdiff_t endpos
+    (string->s || STRINGP (string->lstring)) ? string->schars : ZV;
   struct text_pos pos;
+  int len;
 
   /* If we got past the last known position of display string, compute
      the position of the next one.  That position could be at CHARPOS.  */
@@ -950,7 +961,7 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
     }
   else if (charpos >= *disp_pos && *disp_prop)
     {
-      EMACS_INT disp_end_pos;
+      ptrdiff_t disp_end_pos;
 
       /* We don't expect to find ourselves in the middle of a display
         property.  Hopefully, it will never be needed.  */
@@ -974,6 +985,15 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
          ch = 0xFFFC;
        }
       disp_end_pos = compute_display_string_end (*disp_pos, string);
+      if (disp_end_pos < 0)
+       {
+         /* Somebody removed the display string from the buffer
+            behind our back.  Recover by processing this buffer
+            position as if no display property were present there to
+            begin with.  */
+         *disp_prop = 0;
+         goto normal_char;
+       }
       *nchars = disp_end_pos - *disp_pos;
       if (*nchars <= 0)
        abort ();
@@ -988,9 +1008,9 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
     }
   else
     {
+    normal_char:
       if (string->s)
        {
-         int len;
 
          if (!string->unibyte)
            {
@@ -1005,8 +1025,6 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
        }
       else if (STRINGP (string->lstring))
        {
-         int len;
-
          if (!string->unibyte)
            {
              ch = STRING_CHAR_AND_LENGTH (SDATA (string->lstring) + bytepos,
@@ -1021,8 +1039,8 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
        }
       else
        {
-         ch = FETCH_MULTIBYTE_CHAR (bytepos);
-         *ch_len = CHAR_BYTES (ch);
+         ch = STRING_CHAR_AND_LENGTH (BYTE_POS_ADDR (bytepos), len);
+         *ch_len = len;
        }
       *nchars = 1;
     }
@@ -1050,12 +1068,12 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
    following the buffer position, -1 if position is at the beginning
    of a new paragraph, or -2 if position is neither at beginning nor
    at end of a paragraph.  */
-static EMACS_INT
-bidi_at_paragraph_end (EMACS_INT charpos, EMACS_INT bytepos)
+static ptrdiff_t
+bidi_at_paragraph_end (ptrdiff_t charpos, ptrdiff_t bytepos)
 {
   Lisp_Object sep_re;
   Lisp_Object start_re;
-  EMACS_INT val;
+  ptrdiff_t val;
 
   sep_re = paragraph_separate_re;
   start_re = paragraph_start_re;
@@ -1072,15 +1090,25 @@ bidi_at_paragraph_end (EMACS_INT charpos, EMACS_INT bytepos)
   return val;
 }
 
+/* On my 2005-vintage machine, searching back for paragraph start
+   takes ~1 ms per line.  And bidi_paragraph_init is called 4 times
+   when user types C-p.  The number below limits each call to
+   bidi_paragraph_init to about 10 ms.  */
+#define MAX_PARAGRAPH_SEARCH 7500
+
 /* Find the beginning of this paragraph by looking back in the buffer.
-   Value is the byte position of the paragraph's beginning.  */
-static EMACS_INT
-bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
+   Value is the byte position of the paragraph's beginning, or
+   BEGV_BYTE if paragraph_start_re is still not found after looking
+   back MAX_PARAGRAPH_SEARCH lines in the buffer.  */
+static ptrdiff_t
+bidi_find_paragraph_start (ptrdiff_t pos, ptrdiff_t pos_byte)
 {
   Lisp_Object re = paragraph_start_re;
-  EMACS_INT limit = ZV, limit_byte = ZV_BYTE;
+  ptrdiff_t limit = ZV, limit_byte = ZV_BYTE;
+  ptrdiff_t n = 0;
 
   while (pos_byte > BEGV_BYTE
+        && n++ < MAX_PARAGRAPH_SEARCH
         && fast_looking_at (re, pos, pos_byte, limit, limit_byte, Qnil) < 0)
     {
       /* FIXME: What if the paragraph beginning is covered by a
@@ -1090,9 +1118,17 @@ bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
       pos = find_next_newline_no_quit (pos - 1, -1);
       pos_byte = CHAR_TO_BYTE (pos);
     }
+  if (n >= MAX_PARAGRAPH_SEARCH)
+    pos_byte = BEGV_BYTE;
   return pos_byte;
 }
 
+/* On a 3.4 GHz machine, searching forward for a strong directional
+   character in a long paragraph full of weaks or neutrals takes about
+   1 ms for each 20K characters.  The number below limits each call to
+   bidi_paragraph_init to less than 10 ms even on slow machines.  */
+#define MAX_STRONG_CHAR_SEARCH 100000
+
 /* Determine the base direction, a.k.a. base embedding level, of the
    paragraph we are about to iterate through.  If DIR is either L2R or
    R2L, just use that.  Otherwise, determine the paragraph direction
@@ -1107,18 +1143,18 @@ bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
 
    Note that this function gives the paragraph separator the same
    direction as the preceding paragraph, even though Emacs generally
-   views the separartor as not belonging to any paragraph.  */
+   views the separator as not belonging to any paragraph.  */
 void
 bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
 {
-  EMACS_INT bytepos = bidi_it->bytepos;
+  ptrdiff_t bytepos = bidi_it->bytepos;
   int string_p = bidi_it->string.s != NULL || STRINGP (bidi_it->string.lstring);
-  EMACS_INT pstartbyte;
+  ptrdiff_t pstartbyte;
   /* Note that begbyte is a byte position, while end is a character
      position.  Yes, this is ugly, but we are trying to avoid costly
      calls to BYTE_TO_CHAR and its ilk.  */
-  EMACS_INT begbyte = string_p ? 0 : BEGV_BYTE;
-  EMACS_INT end = string_p ? bidi_it->string.schars : ZV;
+  ptrdiff_t begbyte = string_p ? 0 : BEGV_BYTE;
+  ptrdiff_t end = string_p ? bidi_it->string.schars : ZV;
 
   /* Special case for an empty buffer. */
   if (bytepos == begbyte && bidi_it->charpos == end)
@@ -1140,8 +1176,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
   else if (dir == NEUTRAL_DIR) /* P2 */
     {
       int ch;
-      EMACS_INT ch_len, nchars;
-      EMACS_INT pos, disp_pos = -1;
+      ptrdiff_t ch_len, nchars;
+      ptrdiff_t pos, disp_pos = -1;
       int disp_prop = 0;
       bidi_type_t type;
       const unsigned char *s;
@@ -1162,8 +1198,9 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
         we are potentially in a new paragraph that doesn't yet
         exist.  */
       pos = bidi_it->charpos;
-      s = STRINGP (bidi_it->string.lstring) ?
-       SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+      s = (STRINGP (bidi_it->string.lstring)
+          ? SDATA (bidi_it->string.lstring)
+          : bidi_it->string.s);
       if (bytepos > begbyte
          && bidi_char_at_pos (bytepos, s, bidi_it->string.unibyte) == '\n')
        {
@@ -1187,6 +1224,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
       /* The following loop is run more than once only if NO_DEFAULT_P
         is non-zero, and only if we are iterating on a buffer.  */
       do {
+       ptrdiff_t pos1;
+
        bytepos = pstartbyte;
        if (!string_p)
          pos = BYTE_TO_CHAR (bytepos);
@@ -1195,11 +1234,15 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
                              bidi_it->frame_window_p, &ch_len, &nchars);
        type = bidi_get_type (ch, NEUTRAL_DIR);
 
+       pos1 = pos;
        for (pos += nchars, bytepos += ch_len;
-            (bidi_get_category (type) != STRONG)
-              || (bidi_ignore_explicit_marks_for_paragraph_level
-                  && (type == RLE || type == RLO
-                      || type == LRE || type == LRO));
+            ((bidi_get_category (type) != STRONG)
+             || (bidi_ignore_explicit_marks_for_paragraph_level
+                 && (type == RLE || type == RLO
+                     || type == LRE || type == LRO)))
+              /* Stop when searched too far into an abnormally large
+                 paragraph full of weak or neutral characters.  */
+              && pos - pos1 < MAX_STRONG_CHAR_SEARCH;
             type = bidi_get_type (ch, NEUTRAL_DIR))
          {
            if (pos >= end)
@@ -1236,8 +1279,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
              bidi_it->paragraph_dir = L2R; /* P3 and HL1 */
            else
              {
-               EMACS_INT prevpbyte = pstartbyte;
-               EMACS_INT p = BYTE_TO_CHAR (pstartbyte), pbyte = pstartbyte;
+               ptrdiff_t prevpbyte = pstartbyte;
+               ptrdiff_t p = BYTE_TO_CHAR (pstartbyte), pbyte = pstartbyte;
 
                /* Find the beginning of the previous paragraph, if any.  */
                while (pbyte > BEGV_BYTE && prevpbyte >= pstartbyte)
@@ -1313,9 +1356,10 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
       bidi_it->first_elt = 0;
       if (string_p)
        {
-         const unsigned char *p =
-           STRINGP (bidi_it->string.lstring)
-           ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+         const unsigned char *p
+           = (STRINGP (bidi_it->string.lstring)
+              ? SDATA (bidi_it->string.lstring)
+              : bidi_it->string.s);
 
          if (bidi_it->charpos < 0)
            bidi_it->charpos = 0;
@@ -1411,7 +1455,8 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
              }
          }
        else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
-                || bidi_it->next_en_pos > bidi_it->charpos)
+                || (bidi_it->next_en_pos > bidi_it->charpos
+                    && bidi_it->next_en_type == WEAK_EN))
          type = WEAK_EN;
        break;
       case LRE:        /* X3 */
@@ -1447,7 +1492,8 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
              }
          }
        else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
-                || bidi_it->next_en_pos > bidi_it->charpos)
+                || (bidi_it->next_en_pos > bidi_it->charpos
+                    && bidi_it->next_en_type == WEAK_EN))
          type = WEAK_EN;
        break;
       case PDF:        /* X7 */
@@ -1473,7 +1519,8 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
              }
          }
        else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
-                || bidi_it->next_en_pos > bidi_it->charpos)
+                || (bidi_it->next_en_pos > bidi_it->charpos
+                    && bidi_it->next_en_type == WEAK_EN))
          type = WEAK_EN;
        break;
       default:
@@ -1497,9 +1544,11 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
 {
   int prev_level = bidi_it->level_stack[bidi_it->stack_idx].level;
   int new_level  = bidi_resolve_explicit_1 (bidi_it);
-  EMACS_INT eob = bidi_it->string.s ? bidi_it->string.schars : ZV;
-  const unsigned char *s = STRINGP (bidi_it->string.lstring)
-    ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+  ptrdiff_t eob = bidi_it->string.s ? bidi_it->string.schars : ZV;
+  const unsigned char *s
+    = (STRINGP (bidi_it->string.lstring)
+       ? SDATA (bidi_it->string.lstring)
+       : bidi_it->string.s);
 
   if (prev_level < new_level
       && bidi_it->type == WEAK_BN
@@ -1583,9 +1632,9 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
   int next_char;
   bidi_type_t type_of_next;
   struct bidi_it saved_it;
-  EMACS_INT eob =
-    (STRINGP (bidi_it->string.lstring) || bidi_it->string.s)
-    ? bidi_it->string.schars : ZV;
+  ptrdiff_t eob
+    = ((STRINGP (bidi_it->string.lstring) || bidi_it->string.s)
+       ? bidi_it->string.schars : ZV);
 
   type = bidi_it->type;
   override = bidi_it->level_stack[bidi_it->stack_idx].override;
@@ -1652,15 +1701,15 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
                        && bidi_it->prev.orig_type == WEAK_EN)
                       || bidi_it->prev.type_after_w1 == WEAK_AN)))
        {
-         const unsigned char *s =
-           STRINGP (bidi_it->string.lstring)
-           ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
-
-         next_char =
-           bidi_it->charpos + bidi_it->nchars >= eob
-           ? BIDI_EOB
-           : bidi_char_at_pos (bidi_it->bytepos + bidi_it->ch_len, s,
-                               bidi_it->string.unibyte);
+         const unsigned char *s
+           = (STRINGP (bidi_it->string.lstring)
+              ? SDATA (bidi_it->string.lstring)
+              : bidi_it->string.s);
+
+         next_char = (bidi_it->charpos + bidi_it->nchars >= eob
+                      ? BIDI_EOB
+                      : bidi_char_at_pos (bidi_it->bytepos + bidi_it->ch_len,
+                                          s, bidi_it->string.unibyte));
          type_of_next = bidi_get_type (next_char, override);
 
          if (type_of_next == WEAK_BN
@@ -1703,23 +1752,28 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
       else if (type == WEAK_ET /* W5: ET with EN before or after it */
               || type == WEAK_BN)      /* W5/Retaining */
        {
-         if (bidi_it->prev.type_after_w1 == WEAK_EN /* ET/BN w/EN before it */
-             || bidi_it->next_en_pos > bidi_it->charpos)
+         if (bidi_it->prev.type_after_w1 == WEAK_EN) /* ET/BN w/EN before it */
            type = WEAK_EN;
-         else                  /* W5: ET/BN with EN after it.  */
+         else if (bidi_it->next_en_pos > bidi_it->charpos
+                  && bidi_it->next_en_type != WEAK_BN)
+           {
+             if (bidi_it->next_en_type == WEAK_EN) /* ET/BN with EN after it */
+               type = WEAK_EN;
+           }
+         else if (bidi_it->next_en_pos >=0)
            {
-             EMACS_INT en_pos = bidi_it->charpos + bidi_it->nchars;
-             const unsigned char *s =
-               STRINGP (bidi_it->string.lstring)
-               ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+             ptrdiff_t en_pos = bidi_it->charpos + bidi_it->nchars;
+             const unsigned char *s = (STRINGP (bidi_it->string.lstring)
+                                       ? SDATA (bidi_it->string.lstring)
+                                       : bidi_it->string.s);
 
              if (bidi_it->nchars <= 0)
                abort ();
-             next_char =
-               bidi_it->charpos + bidi_it->nchars >= eob
-               ? BIDI_EOB
-               : bidi_char_at_pos (bidi_it->bytepos + bidi_it->ch_len, s,
-                                   bidi_it->string.unibyte);
+             next_char
+               = (bidi_it->charpos + bidi_it->nchars >= eob
+                  ? BIDI_EOB
+                  : bidi_char_at_pos (bidi_it->bytepos + bidi_it->ch_len, s,
+                                      bidi_it->string.unibyte));
              type_of_next = bidi_get_type (next_char, override);
 
              if (type_of_next == WEAK_ET
@@ -1735,20 +1789,27 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
                  en_pos = bidi_it->charpos;
                  bidi_copy_it (bidi_it, &saved_it);
                }
+             /* Remember this position, to speed up processing of the
+                next ETs.  */
+             bidi_it->next_en_pos = en_pos;
              if (type_of_next == WEAK_EN)
                {
                  /* If the last strong character is AL, the EN we've
                     found will become AN when we get to it (W2). */
-                 if (bidi_it->last_strong.type_after_w1 != STRONG_AL)
-                   {
-                     type = WEAK_EN;
-                     /* Remember this EN position, to speed up processing
-                        of the next ETs.  */
-                     bidi_it->next_en_pos = en_pos;
-                   }
+                 if (bidi_it->last_strong.type_after_w1 == STRONG_AL)
+                   type_of_next = WEAK_AN;
                  else if (type == WEAK_BN)
                    type = NEUTRAL_ON; /* W6/Retaining */
+                 else
+                   type = WEAK_EN;
                }
+             else if (type_of_next == NEUTRAL_B)
+               /* Record the fact that there are no more ENs from
+                  here to the end of paragraph, to avoid entering the
+                  loop above ever again in this paragraph.  */
+               bidi_it->next_en_pos = -1;
+             /* Record the type of the character where we ended our search.  */
+             bidi_it->next_en_type = type_of_next;
            }
        }
     }
@@ -1817,13 +1878,45 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
        || type == NEUTRAL_ON))
     abort ();
 
-  if (bidi_get_category (type) == NEUTRAL
+  if ((type != NEUTRAL_B /* Don't risk entering the long loop below if
+                           we are already at paragraph end.  */
+       && bidi_get_category (type) == NEUTRAL)
       || (type == WEAK_BN && prev_level == current_level))
     {
       if (bidi_it->next_for_neutral.type != UNKNOWN_BT)
        type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
                                       bidi_it->next_for_neutral.type,
                                       current_level);
+      /* The next two "else if" clauses are shortcuts for the
+        important special case when we have a long sequence of
+        neutral or WEAK_BN characters, such as whitespace or nulls or
+        other control characters, on the base embedding level of the
+        paragraph, and that sequence goes all the way to the end of
+        the paragraph and follows a character whose resolved
+        directionality is identical to the base embedding level.
+        (This is what happens in a buffer with plain L2R text that
+        happens to include long sequences of control characters.)  By
+        virtue of N1, the result of examining this long sequence will
+        always be either STRONG_L or STRONG_R, depending on the base
+        embedding level.  So we use this fact directly instead of
+        entering the expensive loop in the "else" clause.  */
+      else if (current_level == 0
+              && bidi_it->prev_for_neutral.type == STRONG_L
+              && !bidi_explicit_dir_char (bidi_it->ch))
+       type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
+                                      STRONG_L, current_level);
+      else if (/* current level is 1 */
+              current_level == 1
+              /* base embedding level is also 1 */
+              && bidi_it->level_stack[0].level == 1
+              /* previous character is one of those considered R for
+                 the purposes of W5 */
+              && (bidi_it->prev_for_neutral.type == STRONG_R
+                  || bidi_it->prev_for_neutral.type == WEAK_EN
+                  || bidi_it->prev_for_neutral.type == WEAK_AN)
+              && !bidi_explicit_dir_char (bidi_it->ch))
+       type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
+                                      STRONG_R, current_level);
       else
        {
          /* Arrrgh!!  The UAX#9 algorithm is too deeply entrenched in
@@ -1864,8 +1957,8 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
                         && bidi_get_category (type) != NEUTRAL)
                     /* This is all per level run, so stop when we
                        reach the end of this level run.  */
-                    || bidi_it->level_stack[bidi_it->stack_idx].level !=
-                    current_level));
+                    || (bidi_it->level_stack[bidi_it->stack_idx].level
+                        != current_level)));
 
          bidi_remember_char (&saved_it.next_for_neutral, bidi_it);
 
@@ -1874,6 +1967,9 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
              case STRONG_L:
              case STRONG_R:
              case STRONG_AL:
+               /* Actually, STRONG_AL cannot happen here, because
+                  bidi_resolve_weak converts it to STRONG_R, per W3.  */
+               eassert (type != STRONG_AL);
                next_type = type;
                break;
              case WEAK_EN:
@@ -1881,7 +1977,6 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
                /* N1: ``European and Arabic numbers are treated as
                   though they were R.''  */
                next_type = STRONG_R;
-               saved_it.next_for_neutral.type = STRONG_R;
                break;
              case WEAK_BN:
                if (!bidi_explicit_dir_char (bidi_it->ch))
@@ -1894,11 +1989,7 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
                   member.  */
                if (saved_it.type != WEAK_BN
                    || bidi_get_category (bidi_it->prev.type_after_w1) == NEUTRAL)
-                 {
-                   next_type = bidi_it->prev_for_neutral.type;
-                   saved_it.next_for_neutral.type = next_type;
-                   bidi_check_type (next_type);
-                 }
+                 next_type = bidi_it->prev_for_neutral.type;
                else
                  {
                    /* This is a BN which does not adjoin neutrals.
@@ -1912,7 +2003,9 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
            }
          type = bidi_resolve_neutral_1 (saved_it.prev_for_neutral.type,
                                         next_type, current_level);
+         saved_it.next_for_neutral.type = next_type;
          saved_it.type = type;
+         bidi_check_type (next_type);
          bidi_check_type (type);
          bidi_copy_it (bidi_it, &saved_it);
        }
@@ -1956,13 +2049,13 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
   bidi_type_t type;
   int level, prev_level = -1;
   struct bidi_saved_info next_for_neutral;
-  EMACS_INT next_char_pos = -2;
+  ptrdiff_t next_char_pos = -2;
 
   if (bidi_it->scan_dir == 1)
     {
-      EMACS_INT eob =
-       (bidi_it->string.s || STRINGP (bidi_it->string.lstring))
-       ? bidi_it->string.schars : ZV;
+      ptrdiff_t eob
+       = ((bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+          ? bidi_it->string.schars : ZV);
 
       /* There's no sense in trying to advance if we hit end of text.  */
       if (bidi_it->charpos >= eob)
@@ -1988,7 +2081,10 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
        bidi_it->next_for_neutral.type = UNKNOWN_BT;
       if (bidi_it->next_en_pos >= 0
          && bidi_it->charpos >= bidi_it->next_en_pos)
-       bidi_it->next_en_pos = -1;
+       {
+         bidi_it->next_en_pos = 0;
+         bidi_it->next_en_type = UNKNOWN_BT;
+       }
       if (bidi_it->next_for_ws.type != UNKNOWN_BT
          && bidi_it->charpos >= bidi_it->next_for_ws.charpos)
        bidi_it->next_for_ws.type = UNKNOWN_BT;
@@ -2007,9 +2103,8 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
      UNKNOWN_BT.  */
   if (bidi_cache_idx > bidi_cache_start && !bidi_it->first_elt)
     {
-      int bob =
-       (bidi_it->string.s || STRINGP (bidi_it->string.lstring)) ? 0 : 1;
-
+      int bob = ((bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+                ? 0 : 1);
       if (bidi_it->scan_dir > 0)
        {
          if (bidi_it->nchars <= 0)
@@ -2087,11 +2182,11 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
       && bidi_it->next_for_ws.type == UNKNOWN_BT)
     {
       int ch;
-      EMACS_INT clen = bidi_it->ch_len;
-      EMACS_INT bpos = bidi_it->bytepos;
-      EMACS_INT cpos = bidi_it->charpos;
-      EMACS_INT disp_pos = bidi_it->disp_pos;
-      EMACS_INT nc = bidi_it->nchars;
+      ptrdiff_t clen = bidi_it->ch_len;
+      ptrdiff_t bpos = bidi_it->bytepos;
+      ptrdiff_t cpos = bidi_it->charpos;
+      ptrdiff_t disp_pos = bidi_it->disp_pos;
+      ptrdiff_t nc = bidi_it->nchars;
       struct bidi_string_data bs = bidi_it->string;
       bidi_type_t chtype;
       int fwp = bidi_it->frame_window_p;
@@ -2102,7 +2197,7 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
       do {
        ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, &dpp, &bs,
                              fwp, &clen, &nc);
-       if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */)
+       if (ch == '\n' || ch == BIDI_EOB)
          chtype = NEUTRAL_B;
        else
          chtype = bidi_get_type (ch, NEUTRAL_DIR);
@@ -2115,7 +2210,7 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
     }
 
   /* Resolve implicit levels, with a twist: PDFs get the embedding
-     level of the enbedding they terminate.  See below for the
+     level of the embedding they terminate.  See below for the
      reason.  */
   if (bidi_it->orig_type == PDF
       /* Don't do this if this formatting code didn't change the
@@ -2152,7 +2247,6 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
   else if (bidi_it->orig_type == NEUTRAL_B /* L1 */
           || bidi_it->orig_type == NEUTRAL_S
           || bidi_it->ch == '\n' || bidi_it->ch == BIDI_EOB
-          /* || bidi_it->ch == LINESEP_CHAR */
           || (bidi_it->orig_type == NEUTRAL_WS
               && (bidi_it->next_for_ws.type == NEUTRAL_B
                   || bidi_it->next_for_ws.type == NEUTRAL_S)))
@@ -2240,7 +2334,8 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
     GCPRO1 (bidi_it->string.lstring);
 
   /* If we just passed a newline, initialize for the next line.  */
-  if (!bidi_it->first_elt && bidi_it->orig_type == NEUTRAL_B)
+  if (!bidi_it->first_elt
+      && (bidi_it->ch == '\n' || bidi_it->ch == BIDI_EOB))
     bidi_line_init (bidi_it);
 
   /* Prepare the sentinel iterator state, and cache it.  When we bump
@@ -2321,7 +2416,8 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
      reordering, whereas we _must_ know the paragraph base direction
      _before_ we process the paragraph's text, since the base
      direction affects the reordering.  */
-  if (bidi_it->scan_dir == 1 && bidi_it->orig_type == NEUTRAL_B)
+  if (bidi_it->scan_dir == 1
+      && (bidi_it->ch == '\n' || bidi_it->ch == BIDI_EOB))
     {
       /* The paragraph direction of the entire string, once
         determined, is in effect for the entire string.  Setting the
@@ -2332,9 +2428,9 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
        bidi_it->separator_limit = bidi_it->string.schars;
       else if (bidi_it->bytepos < ZV_BYTE)
        {
-         EMACS_INT sep_len =
-           bidi_at_paragraph_end (bidi_it->charpos + bidi_it->nchars,
-                                  bidi_it->bytepos + bidi_it->ch_len);
+         ptrdiff_t sep_len
+           bidi_at_paragraph_end (bidi_it->charpos + bidi_it->nchars,
+                                    bidi_it->bytepos + bidi_it->ch_len);
          if (bidi_it->nchars <= 0)
            abort ();
          if (sep_len >= 0)
@@ -2342,8 +2438,8 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
              bidi_it->new_paragraph = 1;
              /* Record the buffer position of the last character of the
                 paragraph separator.  */
-             bidi_it->separator_limit =
-               bidi_it->charpos + bidi_it->nchars + sep_len;
+             bidi_it->separator_limit
+               bidi_it->charpos + bidi_it->nchars + sep_len;
            }
        }
     }
@@ -2398,6 +2494,6 @@ bidi_dump_cached_states (void)
   fputs ("\n", stderr);
   fputs ("pos ", stderr);
   for (i = 0; i < bidi_cache_idx; i++)
-    fprintf (stderr, "%*"pI"d", ndigits, bidi_cache[i].charpos);
+    fprintf (stderr, "%*"pD"d", ndigits, bidi_cache[i].charpos);
   fputs ("\n", stderr);
 }