]> code.delx.au - gnu-emacs/blobdiff - src/bidi.c
Merge from origin/emacs-24
[gnu-emacs] / src / bidi.c
index bbafc785e7bb8cc6efbaa8614d98d2b4b694db6f..e5e08c6a25297cd39326d02306e7fe6b14d7aa79 100644 (file)
@@ -1,5 +1,5 @@
 /* Low-level bidirectional buffer/string-scanning functions for GNU Emacs.
-   Copyright (C) 2000-2001, 2004-2005, 2009-2014 Free Software
+   Copyright (C) 2000-2001, 2004-2005, 2009-2015 Free Software
    Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -262,7 +262,6 @@ typedef enum {
 } bidi_category_t;
 
 static Lisp_Object paragraph_start_re, paragraph_separate_re;
-static Lisp_Object Qparagraph_start, Qparagraph_separate;
 
 \f
 /***********************************************************************
@@ -433,6 +432,9 @@ bidi_set_sos_type (struct bidi_it *bidi_it, int level_before, int level_after)
     = bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
 }
 
+#define ISOLATE_STATUS(BIDI_IT, IDX)  ((BIDI_IT)->level_stack[IDX].flags & 1)
+#define OVERRIDE(BIDI_IT, IDX)  (((BIDI_IT)->level_stack[IDX].flags >> 1) & 3)
+
 /* Push the current embedding level and override status; reset the
    current level to LEVEL and the current override status to OVERRIDE.  */
 static void
@@ -447,14 +449,14 @@ bidi_push_embedding_level (struct bidi_it *bidi_it,
   st = &bidi_it->level_stack[bidi_it->stack_idx];
   eassert (level <= (1 << 7));
   st->level = level;
-  st->override = override;
-  st->isolate_status = isolate_status;
+  st->flags = (((override & 3) << 1) | (isolate_status != 0));
   if (isolate_status)
     {
-      st->last_strong = bidi_it->last_strong;
-      st->prev_for_neutral = bidi_it->prev_for_neutral;
-      st->next_for_neutral = bidi_it->next_for_neutral;
-      st->sos = bidi_it->sos;
+      st->last_strong_type = bidi_it->last_strong.type;
+      st->prev_for_neutral_type = bidi_it->prev_for_neutral.type;
+      st->next_for_neutral_type = bidi_it->next_for_neutral.type;
+      st->next_for_neutral_pos = bidi_it->next_for_neutral.charpos;
+      st->flags |= ((bidi_it->sos == L2R ? 0 : 1) << 3);
     }
   /* We've got a new isolating sequence, compute the directional type
      of sos and initialize per-sequence variables (UAX#9, clause X10).  */
@@ -473,8 +475,7 @@ bidi_pop_embedding_level (struct bidi_it *bidi_it)
      and PDIs (X6a, 2nd bullet).  */
   if (bidi_it->stack_idx > 0)
     {
-      bool isolate_status
-       = bidi_it->level_stack[bidi_it->stack_idx].isolate_status;
+      bool isolate_status = ISOLATE_STATUS (bidi_it, bidi_it->stack_idx);
       int old_level = bidi_it->level_stack[bidi_it->stack_idx].level;
 
       struct bidi_stack st;
@@ -482,6 +483,7 @@ bidi_pop_embedding_level (struct bidi_it *bidi_it)
       st = bidi_it->level_stack[bidi_it->stack_idx];
       if (isolate_status)
        {
+         bidi_dir_t sos = ((st.flags >> 3) & 1);
          /* PREV is used in W1 for resolving WEAK_NSM.  By the time
             we get to an NSM, we must have gotten past at least one
             character: the PDI that ends the isolate from which we
@@ -490,10 +492,11 @@ bidi_pop_embedding_level (struct bidi_it *bidi_it)
             UNKNOWN_BT to be able to catch any blunders in this
             logic.  */
          bidi_it->prev.orig_type = bidi_it->prev.type = UNKNOWN_BT;
-         bidi_it->last_strong = st.last_strong;
-         bidi_it->prev_for_neutral = st.prev_for_neutral;
-         bidi_it->next_for_neutral = st.next_for_neutral;
-         bidi_it->sos = st.sos;
+         bidi_it->last_strong.type = st.last_strong_type;
+         bidi_it->prev_for_neutral.type = st.prev_for_neutral_type;
+         bidi_it->next_for_neutral.type = st.next_for_neutral_type;
+         bidi_it->next_for_neutral.charpos = st.next_for_neutral_pos;
+         bidi_it->sos = (sos == 0 ? L2R : R2L);
        }
       else
        bidi_set_sos_type (bidi_it, old_level,
@@ -542,6 +545,30 @@ bidi_copy_it (struct bidi_it *to, struct bidi_it *from)
    characters).  200 was chosen as an upper limit for reasonably-long
    lines in a text file/buffer.  */
 #define BIDI_CACHE_CHUNK 200
+/* Maximum size we allow the cache to become, per iterator stack slot,
+   in units of struct bidi_it size.  If we allow unlimited growth, we
+   could run out of memory for pathologically long bracketed text or
+   very long text lines that need to be reordered.  This is aggravated
+   when word-wrap is in effect, since then functions display_line and
+   move_it_in_display_line_to need to keep up to 4 copies of the
+   cache.
+
+   This limitation means there can be no more than that amount of
+   contiguous RTL text on any single physical line in a LTR paragraph,
+   and similarly with contiguous LTR + numeric text in a RTL
+   paragraph.  (LTR text in a LTR paragraph and RTL text in a RTL
+   paragraph are not reordered, and so don't need the cache, and
+   cannot hit this limit.)  More importantly, no single line can have
+   text longer than this inside paired brackets (because bracket pairs
+   resolution uses the cache).  If the limit is exceeded, the fallback
+   code will produce visual order that will be incorrect if there are
+   RTL characters in the offending line of text.  */
+/* Do we need to allow customization of this limit?  */
+#define BIDI_CACHE_MAX_ELTS_PER_SLOT 50000
+#if BIDI_CACHE_CHUNK >= BIDI_CACHE_MAX_ELTS_PER_SLOT
+# error BIDI_CACHE_CHUNK must be less than BIDI_CACHE_MAX_ELTS_PER_SLOT
+#endif
+static ptrdiff_t bidi_cache_max_elts = BIDI_CACHE_MAX_ELTS_PER_SLOT;
 static struct bidi_it *bidi_cache;
 static ptrdiff_t bidi_cache_size = 0;
 enum { elsz = sizeof (struct bidi_it) };
@@ -562,9 +589,19 @@ 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))
+        + sizeof (bidi_cache_last_idx) + sizeof (bidi_cache_max_elts))
   };
 
+/* Effectively remove the cached states beyond the Nth state from the
+   part of the cache relevant to iteration of the current object
+   (buffer or string).  */
+static void
+bidi_cache_reset_to (int n)
+{
+  bidi_cache_idx = bidi_cache_start + n;
+  bidi_cache_last_idx = -1;
+}
+
 /* Reset the cache state to the empty state.  We only reset the part
    of the cache relevant to iteration of the current object.  Previous
    objects, which are pushed on the display iterator's stack, are left
@@ -574,8 +611,7 @@ enum
 static void
 bidi_cache_reset (void)
 {
-  bidi_cache_idx = bidi_cache_start;
-  bidi_cache_last_idx = -1;
+  bidi_cache_reset_to (0);
 }
 
 /* Shrink the cache to its minimal size.  Called when we init the bidi
@@ -591,6 +627,7 @@ bidi_cache_shrink (void)
       bidi_cache_size = BIDI_CACHE_CHUNK;
     }
   bidi_cache_reset ();
+  bidi_cache_max_elts = BIDI_CACHE_MAX_ELTS_PER_SLOT;
 }
 
 static void
@@ -609,7 +646,9 @@ bidi_cache_fetch_state (ptrdiff_t idx, struct bidi_it *bidi_it)
 /* Find a cached state with a given CHARPOS and resolved embedding
    level less or equal to LEVEL.  If LEVEL is -1, disregard the
    resolved levels in cached states.  DIR, if non-zero, means search
-   in that direction from the last cache hit.  */
+   in that direction from the last cache hit.
+
+   Value is the index of the cached state, or -1 if not found.  */
 static ptrdiff_t
 bidi_cache_search (ptrdiff_t charpos, int level, int dir)
 {
@@ -683,7 +722,8 @@ bidi_cache_find_level_change (int level, int dir, bool before)
       ptrdiff_t i = dir ? bidi_cache_last_idx : bidi_cache_idx - 1;
       int incr = before ? 1 : 0;
 
-      eassert (!dir || bidi_cache_last_idx >= 0);
+      if (i < 0)  /* cache overflowed? */
+       i = 0;
 
       if (!dir)
        dir = -1;
@@ -721,23 +761,37 @@ bidi_cache_ensure_space (ptrdiff_t idx)
   /* Enlarge the cache as needed.  */
   if (idx >= bidi_cache_size)
     {
-      /* 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 chunk_size = BIDI_CACHE_CHUNK;
 
-      /* Also, it cannot be larger than what C can represent.  */
-      ptrdiff_t c_bound
-       = (min (PTRDIFF_MAX, SIZE_MAX) - bidi_shelve_header_size) / elsz;
+      if (bidi_cache_size > bidi_cache_max_elts - chunk_size)
+       chunk_size = bidi_cache_max_elts - bidi_cache_size;
 
-      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);
+      if (max (idx + 1,
+              bidi_cache_size + chunk_size) <= bidi_cache_max_elts)
+       {
+         /* 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);
+
+         /* 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 max_elts = bidi_cache_max_elts;
+
+         max_elts = min (max_elts, min (string_or_buffer_bound, c_bound));
+
+         /* Force xpalloc not to over-allocate by passing it MAX_ELTS
+            as its 4th argument.  */
+         bidi_cache = xpalloc (bidi_cache, &bidi_cache_size,
+                               max (chunk_size, idx - bidi_cache_size + 1),
+                               max_elts, elsz);
+         eassert (bidi_cache_size > idx);
+       }
     }
 }
 
-static void
+static int
 bidi_cache_iterator_state (struct bidi_it *bidi_it, bool resolved,
                           bool update_only)
 {
@@ -749,7 +803,7 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, bool resolved,
   idx = bidi_cache_search (bidi_it->charpos, -1, 1);
 
   if (idx < 0 && update_only)
-    return;
+    return 0;
 
   if (idx < 0)
     {
@@ -758,19 +812,23 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, bool resolved,
       /* Character positions should correspond to cache positions 1:1.
         If we are outside the range of cached positions, the cache is
         useless and must be reset.  */
-      if (idx > bidi_cache_start &&
-         (bidi_it->charpos > (bidi_cache[idx - 1].charpos
-                              + bidi_cache[idx - 1].nchars)
-          || bidi_it->charpos < bidi_cache[bidi_cache_start].charpos))
+      if (bidi_cache_start < idx && idx < bidi_cache_size
+         && (bidi_it->charpos > (bidi_cache[idx - 1].charpos
+                                 + bidi_cache[idx - 1].nchars)
+             || bidi_it->charpos < bidi_cache[bidi_cache_start].charpos))
        {
          bidi_cache_reset ();
          idx = bidi_cache_start;
        }
       if (bidi_it->nchars <= 0)
        emacs_abort ();
-      bidi_copy_it (&bidi_cache[idx], bidi_it);
-      if (!resolved)
-       bidi_cache[idx].resolved_level = -1;
+      /* Don't cache if no available space in the cache.  */
+      if (bidi_cache_size > idx)
+       {
+         bidi_copy_it (&bidi_cache[idx], bidi_it);
+         if (!resolved)
+           bidi_cache[idx].resolved_level = -1;
+       }
     }
   else
     {
@@ -793,33 +851,39 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, bool resolved,
       bidi_cache[idx].bracket_enclosed_type = bidi_it->bracket_enclosed_type;
     }
 
-  bidi_cache_last_idx = idx;
-  if (idx >= bidi_cache_idx)
-    bidi_cache_idx = idx + 1;
+  if (bidi_cache_size > idx)
+    {
+      bidi_cache_last_idx = idx;
+      if (idx >= bidi_cache_idx)
+       bidi_cache_idx = idx + 1;
+      return 1;
+    }
+  else
+    {
+      /* The cache overflowed.  */
+      bidi_cache_last_idx = -1;
+      return 0;
+    }
 }
 
 /* Look for a cached iterator state that corresponds to CHARPOS.  If
    found, copy the cached state into BIDI_IT and return the type of
-   the cached entry.  If not found, return UNKNOWN_BT.  NEUTRALS_OK
-   non-zero means it is OK to return cached state for neutral
-   characters that have no valid next_for_neutral member, and
-   therefore cannot be resolved.  This can happen if the state was
-   cached before it was resolved in bidi_resolve_neutral.  */
+   the cached entry.  If not found, return UNKNOWN_BT.  RESOLVED_ONLY
+   zero means it is OK to return cached states that were not fully
+   resolved yet.  This can happen if the state was cached before it
+   was resolved in bidi_resolve_neutral.  */
 static bidi_type_t
-bidi_cache_find (ptrdiff_t charpos, bool neutrals_ok, struct bidi_it *bidi_it)
+bidi_cache_find (ptrdiff_t charpos, bool resolved_only, struct bidi_it *bidi_it)
 {
   ptrdiff_t i = bidi_cache_search (charpos, -1, bidi_it->scan_dir);
 
   if (i >= bidi_cache_start
-      && (neutrals_ok
-         /* Callers that don't want to resolve neutrals (and set
-            neutrals_ok = false) need to be sure that there's enough
-            info in the cached state to resolve the neutrals and
-            isolates, and if not, they don't want the cached state.  */
-         || !(bidi_cache[i].resolved_level == -1
-              && (bidi_get_category (bidi_cache[i].type) == NEUTRAL
-                  || bidi_isolate_fmt_char (bidi_cache[i].type))
-              && bidi_cache[i].next_for_neutral.type == UNKNOWN_BT)))
+      && (!resolved_only
+         /* Callers that want only fully resolved states (and set
+            resolved_only = true) need to be sure that there's enough
+            info in the cached state to return the state as final,
+            and if not, they don't want the cached state.  */
+         || bidi_cache[i].resolved_level >= 0))
     {
       bidi_dir_t current_scan_dir = bidi_it->scan_dir;
 
@@ -837,8 +901,13 @@ bidi_cache_find (ptrdiff_t charpos, bool neutrals_ok, struct bidi_it *bidi_it)
 static int
 bidi_peek_at_next_level (struct bidi_it *bidi_it)
 {
-  if (bidi_cache_idx == bidi_cache_start || bidi_cache_last_idx == -1)
+  if (bidi_cache_idx == bidi_cache_start)
     emacs_abort ();
+  /* If the cache overflowed, return the level of the last cached
+     character.  */
+  if (bidi_cache_last_idx == -1
+      || (bidi_cache_last_idx >= bidi_cache_idx - 1 && bidi_it->scan_dir > 0))
+    return bidi_cache[bidi_cache_idx - 1].resolved_level;
   return bidi_cache[bidi_cache_last_idx + bidi_it->scan_dir].resolved_level;
 }
 
@@ -855,6 +924,8 @@ bidi_peek_at_next_level (struct bidi_it *bidi_it)
 void
 bidi_push_it (struct bidi_it *bidi_it)
 {
+  /* Give this stack slot its cache room.  */
+  bidi_cache_max_elts += BIDI_CACHE_MAX_ELTS_PER_SLOT;
   /* Save the current iterator state in its entirety after the last
      used cache slot.  */
   bidi_cache_ensure_space (bidi_cache_idx);
@@ -891,6 +962,9 @@ bidi_pop_it (struct bidi_it *bidi_it)
 
   /* Invalidate the last-used cache slot data.  */
   bidi_cache_last_idx = -1;
+
+  bidi_cache_max_elts -= BIDI_CACHE_MAX_ELTS_PER_SLOT;
+  eassert (bidi_cache_max_elts > 0);
 }
 
 static ptrdiff_t bidi_cache_total_alloc;
@@ -930,6 +1004,11 @@ bidi_shelve_cache (void)
          + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
          + sizeof (bidi_cache_start),
          &bidi_cache_last_idx, sizeof (bidi_cache_last_idx));
+  memcpy (databuf + sizeof (bidi_cache_idx)
+         + bidi_cache_idx * sizeof (struct bidi_it)
+         + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
+         + sizeof (bidi_cache_start) + sizeof (bidi_cache_last_idx),
+         &bidi_cache_max_elts, sizeof (bidi_cache_max_elts));
 
   return databuf;
 }
@@ -951,6 +1030,7 @@ bidi_unshelve_cache (void *databuf, bool just_free)
          /* A NULL pointer means an empty cache.  */
          bidi_cache_start = 0;
          bidi_cache_sp = 0;
+         bidi_cache_max_elts = BIDI_CACHE_MAX_ELTS_PER_SLOT;
          bidi_cache_reset ();
        }
     }
@@ -990,6 +1070,12 @@ bidi_unshelve_cache (void *databuf, bool just_free)
                  + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
                  + sizeof (bidi_cache_start),
                  sizeof (bidi_cache_last_idx));
+         memcpy (&bidi_cache_max_elts,
+                 p + sizeof (bidi_cache_idx)
+                 + bidi_cache_idx * sizeof (struct bidi_it)
+                 + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
+                 + sizeof (bidi_cache_start) + sizeof (bidi_cache_last_idx),
+                 sizeof (bidi_cache_max_elts));
          bidi_cache_total_alloc
            -= (bidi_shelve_header_size
                + bidi_cache_idx * sizeof (struct bidi_it));
@@ -1021,14 +1107,12 @@ bidi_initialize (void)
     emacs_abort ();
   staticpro (&bidi_brackets_table);
 
-  Qparagraph_start = intern ("paragraph-start");
-  staticpro (&Qparagraph_start);
+  DEFSYM (Qparagraph_start, "paragraph-start");
   paragraph_start_re = Fsymbol_value (Qparagraph_start);
   if (!STRINGP (paragraph_start_re))
     paragraph_start_re = build_string ("\f\\|[ \t]*$");
   staticpro (&paragraph_start_re);
-  Qparagraph_separate = intern ("paragraph-separate");
-  staticpro (&Qparagraph_separate);
+  DEFSYM (Qparagraph_separate, "paragraph-separate");
   paragraph_separate_re = Fsymbol_value (Qparagraph_separate);
   if (!STRINGP (paragraph_separate_re))
     paragraph_separate_re = build_string ("[ \t\f]*$");
@@ -1036,6 +1120,7 @@ bidi_initialize (void)
 
   bidi_cache_sp = 0;
   bidi_cache_total_alloc = 0;
+  bidi_cache_max_elts = BIDI_CACHE_MAX_ELTS_PER_SLOT;
 
   bidi_initialized = 1;
 }
@@ -1080,6 +1165,7 @@ bidi_init_it (ptrdiff_t charpos, ptrdiff_t bytepos, bool frame_window_p,
   bidi_it->prev_for_neutral.charpos = -1;
   bidi_it->prev_for_neutral.type
     = bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
+  bidi_it->bracket_pairing_pos = -1;
   bidi_it->sos = L2R;   /* FIXME: should it be user-selectable? */
   bidi_it->disp_pos = -1;      /* invalid/unknown */
   bidi_it->disp_prop = 0;
@@ -1098,8 +1184,7 @@ bidi_line_init (struct bidi_it *bidi_it)
   bidi_it->scan_dir = 1; /* FIXME: do we need to have control on this? */
   bidi_it->stack_idx = 0;
   bidi_it->resolved_level = bidi_it->level_stack[0].level;
-  bidi_it->level_stack[0].override = NEUTRAL_DIR; /* X1 */
-  bidi_it->level_stack[0].isolate_status = false; /* X1 */
+  bidi_it->level_stack[0].flags = 0; /* NEUTRAL_DIR, false per X1 */
   bidi_it->invalid_levels = 0;
   bidi_it->isolate_level = 0;   /* X1 */
   bidi_it->invalid_isolates = 0; /* X1 */
@@ -1109,6 +1194,7 @@ bidi_line_init (struct bidi_it *bidi_it)
   bidi_it->next_en_type = UNKNOWN_BT;
   bidi_it->next_for_ws.charpos = -1;
   bidi_it->next_for_ws.type = UNKNOWN_BT;
+  bidi_it->bracket_pairing_pos = -1;
   bidi_set_sos_type (bidi_it,
                     (bidi_it->paragraph_dir == R2L ? 1 : 0),
                     bidi_it->level_stack[0].level); /* X10 */
@@ -1713,6 +1799,11 @@ bidi_explicit_dir_char (int ch)
 
   if (!bidi_initialized)
     emacs_abort ();
+  if (ch < 0)
+    {
+      eassert (ch == BIDI_EOB);
+      return false;
+    }
   ch_type = (bidi_type_t) XINT (CHAR_TABLE_REF (bidi_type_table, ch));
   return (ch_type == LRE || ch_type == LRO
          || ch_type == RLE || ch_type == RLO
@@ -1736,6 +1827,9 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
   bool string_p = bidi_it->string.s || STRINGP (bidi_it->string.lstring);
   ptrdiff_t ch_len, nchars, disp_pos, end;
   int disp_prop;
+  ptrdiff_t eob
+    = ((bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+       ? bidi_it->string.schars : ZV);
 
   /* Record the info about the previous character.  */
   if (bidi_it->type_after_wn != WEAK_BN /* W1/Retaining */
@@ -1762,7 +1856,14 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
   /* If we overstepped the characters used for resolving neutrals
      and whitespace, invalidate their info in the iterator.  */
   if (bidi_it->charpos >= bidi_it->next_for_neutral.charpos)
-    bidi_it->next_for_neutral.type = UNKNOWN_BT;
+    {
+      bidi_it->next_for_neutral.type = UNKNOWN_BT;
+      /* If needed, reset the "magical" value of pairing bracket
+        position, so that bidi_resolve_brackets will resume
+        resolution of brackets according to BPA.  */
+      if (bidi_it->bracket_pairing_pos == eob)
+       bidi_it->bracket_pairing_pos = -1;
+    }
   if (bidi_it->next_en_pos >= 0
       && bidi_it->charpos >= bidi_it->next_en_pos)
     {
@@ -1770,9 +1871,14 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
       bidi_it->next_en_type = UNKNOWN_BT;
     }
 
-  /* Reset the bracket resolution info.  */
-  bidi_it->bracket_pairing_pos = -1;
-  bidi_it->bracket_enclosed_type = UNKNOWN_BT;
+  /* Reset the bracket resolution info, unless we previously decided
+     (in bidi_find_bracket_pairs) that brackets in this level run
+     should be resolved as neutrals.  */
+  if (bidi_it->bracket_pairing_pos != eob)
+    {
+      bidi_it->bracket_pairing_pos = -1;
+      bidi_it->bracket_enclosed_type = UNKNOWN_BT;
+    }
 
   /* If reseat()'ed, don't advance, so as to start iteration from the
      position where we were reseated.  bidi_it->bytepos can be less
@@ -1803,9 +1909,9 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
            }
          eassert (bidi_it->bytepos == CHAR_TO_BYTE (bidi_it->charpos));
        }
-      /* Determine the orginal bidi type of the previous character,
+      /* Determine the original bidi type of the previous character,
         which is needed for handling isolate initiators and PDF.  The
-        type of the previous character will only be non-trivial if
+        type of the previous character will be non-trivial only if
         our caller moved through some previous text in
         get_visually_first_element, in which case bidi_it->prev holds
         the information we want.  */
@@ -1836,8 +1942,8 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
     prev_type = NEUTRAL_B;
 
   current_level = bidi_it->level_stack[bidi_it->stack_idx].level; /* X1 */
-  override = bidi_it->level_stack[bidi_it->stack_idx].override;
-  isolate_status = bidi_it->level_stack[bidi_it->stack_idx].isolate_status;
+  isolate_status = ISOLATE_STATUS (bidi_it, bidi_it->stack_idx);
+  override = OVERRIDE (bidi_it, bidi_it->stack_idx);
   new_level = current_level;
 
   if (bidi_it->charpos >= (string_p ? bidi_it->string.schars : ZV))
@@ -2011,7 +2117,7 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
       else if (bidi_it->isolate_level > 0)
        {
          bidi_it->invalid_levels = 0;
-         while (!bidi_it->level_stack[bidi_it->stack_idx].isolate_status)
+         while (!ISOLATE_STATUS (bidi_it, bidi_it->stack_idx))
            bidi_pop_embedding_level (bidi_it);
          eassert (bidi_it->stack_idx > 0);
          new_level = bidi_pop_embedding_level (bidi_it);
@@ -2019,12 +2125,15 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
        }
       bidi_it->resolved_level = new_level;
       /* Unicode 8.0 correction.  */
-      if (bidi_it->level_stack[bidi_it->stack_idx].override == L2R)
-       bidi_it->type_after_wn = STRONG_L;
-      else if (bidi_it->level_stack[bidi_it->stack_idx].override == R2L)
-       bidi_it->type_after_wn = STRONG_R;
-      else
-       bidi_it->type_after_wn = type;
+      {
+       bidi_dir_t stack_override = OVERRIDE (bidi_it, bidi_it->stack_idx);
+       if (stack_override == L2R)
+         bidi_it->type_after_wn = STRONG_L;
+       else if (stack_override == R2L)
+         bidi_it->type_after_wn = STRONG_R;
+       else
+         bidi_it->type_after_wn = type;
+      }
       break;
     case PDF:  /* X7 */
       bidi_it->type_after_wn = type;
@@ -2067,7 +2176,7 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
        ? bidi_it->string.schars : ZV);
 
   type = bidi_it->type;
-  override = bidi_it->level_stack[bidi_it->stack_idx].override;
+  override = OVERRIDE (bidi_it, bidi_it->stack_idx);
 
   eassert (!(type == UNKNOWN_BT
             || type == LRE
@@ -2340,7 +2449,42 @@ typedef struct bpa_stack_entry {
 
 /* With MAX_ALLOCA of 16KB, this should allow at least 1K slots in the
    BPA stack, which should be more than enough for actual bidi text.  */
-#define MAX_BPA_STACK (max (MAX_ALLOCA / sizeof (bpa_stack_entry), 1))
+#define MAX_BPA_STACK ((int)max (MAX_ALLOCA / sizeof (bpa_stack_entry), 1))
+
+/* UAX#9 says to match opening brackets with the matching closing
+   brackets or their canonical equivalents.  As of Unicode 7.0, there
+   are only 2 bracket characters that have canonical equivalence
+   decompositions: u+2329 and u+232A.  So instead of accessing the
+   table in uni-decomposition.el, we just handle these 2 characters
+   with this simple macro.  Note that ASCII characters don't have
+   canonical equivalents by definition.  */
+
+/* To find all the characters that need to be processed by
+   CANONICAL_EQU, first find all the characters which have
+   decompositions in UnicodeData.txt, with this Awk script:
+
+    awk -F ";" " {if ($6 != \"\") print $1, $6}" UnicodeData.txt
+
+   Then produce a list of all the bracket characters in BidiBrackets.txt:
+
+    awk -F "[ ;]" " {if ($1 != \"#\" && $1 != \"\") print $1}" BidiBrackets.txt
+
+   And finally, cross-reference these two:
+
+    fgrep -w -f brackets.txt decompositions.txt
+
+   where "decompositions.txt" was produced by the 1st script, and
+   "brackets.txt" by the 2nd script.  In the output of fgrep, look
+   only for decompositions that don't begin with some compatibility
+   formatting tag, such as "<compat>".  Only decompositions that
+   consist solely of character codepoints are relevant to bidi
+   brackets processing.  */
+
+#define CANONICAL_EQU(c)                                       \
+  ( ASCII_CHAR_P (c) ? c                                       \
+    : (c) == 0x2329 ? 0x3008                                   \
+    : (c) == 0x232a ? 0x3009                                   \
+    : c )
 
 #ifdef ENABLE_CHECKING
 # define STORE_BRACKET_CHARPOS \
@@ -2351,16 +2495,16 @@ typedef struct bpa_stack_entry {
 
 #define PUSH_BPA_STACK                                                 \
   do {                                                                 \
-   bpa_sp++;                                                           \
-   if (bpa_sp >= MAX_BPA_STACK)                                                \
-     {                                                                 \
-       bpa_sp = MAX_BPA_STACK - 1;                                     \
-       goto bpa_give_up;                                               \
-     }                                                                 \
-   bpa_stack[bpa_sp].close_bracket_char = bidi_mirror_char (bidi_it->ch); \
-   bpa_stack[bpa_sp].open_bracket_idx = bidi_cache_last_idx;           \
-   bpa_stack[bpa_sp].flags = 0;                                                \
-   STORE_BRACKET_CHARPOS;                                              \
+    int ch;                                                            \
+    if (bpa_sp < MAX_BPA_STACK - 1)                                    \
+      {                                                                        \
+       bpa_sp++;                                                       \
+       ch = CANONICAL_EQU (bidi_it->ch);                               \
+       bpa_stack[bpa_sp].close_bracket_char = bidi_mirror_char (ch);   \
+       bpa_stack[bpa_sp].open_bracket_idx = bidi_cache_last_idx;       \
+       bpa_stack[bpa_sp].flags = 0;                                    \
+       STORE_BRACKET_CHARPOS;                                          \
+      }                                                                        \
   } while (0)
 
 
@@ -2389,9 +2533,14 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
       bpa_stack_entry bpa_stack[MAX_BPA_STACK];
       int bpa_sp = -1;
       struct bidi_it saved_it;
+      int base_level = bidi_it->level_stack[0].level;
       int embedding_level = bidi_it->level_stack[bidi_it->stack_idx].level;
+      int maxlevel = embedding_level;
       bidi_type_t embedding_type = (embedding_level & 1) ? STRONG_R : STRONG_L;
       struct bidi_it tem_it;
+      bool l2r_seen = false, r2l_seen = false;
+      ptrdiff_t pairing_pos;
+      int idx_at_entry = bidi_cache_idx;
 
       eassert (MAX_BPA_STACK >= 100);
       bidi_copy_it (&saved_it, bidi_it);
@@ -2405,13 +2554,32 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
          int old_sidx, new_sidx;
          int current_level = bidi_it->level_stack[bidi_it->stack_idx].level;
 
-         bidi_cache_iterator_state (bidi_it, type == NEUTRAL_B, 0);
+         if (maxlevel < current_level)
+           maxlevel = current_level;
+         /* Mark every opening bracket character we've traversed by
+            putting its own position into bracket_pairing_pos.  This
+            is examined in bidi_resolve_brackets to distinguish
+            brackets that were already resolved to stay NEUTRAL_ON,
+            and those that were not yet processed by this function
+            (because they were skipped when we skip higher embedding
+            levels below).  */
+         if (btype == BIDI_BRACKET_OPEN && bidi_it->bracket_pairing_pos == -1)
+           bidi_it->bracket_pairing_pos = bidi_it->charpos;
+         if (!bidi_cache_iterator_state (bidi_it, type == NEUTRAL_B, 0))
+           {
+             /* No more space in cache -- give up and let the opening
+                bracket that started this be processed as a
+                NEUTRAL_ON.  */
+             bidi_cache_reset_to (idx_at_entry - bidi_cache_start);
+             bidi_copy_it (bidi_it, &saved_it);
+             goto give_up;
+           }
          if (btype == BIDI_BRACKET_OPEN)
            PUSH_BPA_STACK;
          else if (btype == BIDI_BRACKET_CLOSE)
            {
              int sp = bpa_sp;
-             int curchar = bidi_it->ch;
+             int curchar = CANONICAL_EQU (bidi_it->ch);
 
              eassert (sp >= 0);
              while (sp >= 0 && bpa_stack[sp].close_bracket_char != curchar)
@@ -2461,6 +2629,7 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
                  flag = ((embedding_level & 1) == 0
                          ? FLAG_EMBEDDING_INSIDE
                          : FLAG_OPPOSITE_INSIDE);
+                 l2r_seen = true;
                  break;
                case STRONG_R:
                case WEAK_EN:
@@ -2468,6 +2637,7 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
                  flag = ((embedding_level & 1) == 1
                          ? FLAG_EMBEDDING_INSIDE
                          : FLAG_OPPOSITE_INSIDE);
+                 r2l_seen = true;
                  break;
                default:
                  break;
@@ -2483,14 +2653,25 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
          /* Skip level runs excluded from this isolating run sequence.  */
          new_sidx = bidi_it->stack_idx;
          if (bidi_it->level_stack[new_sidx].level > current_level
-             && (bidi_it->level_stack[new_sidx].isolate_status
+             && (ISOLATE_STATUS (bidi_it, new_sidx)
                  || (new_sidx > old_sidx + 1
-                     && bidi_it->level_stack[new_sidx - 1].isolate_status)))
+                     && ISOLATE_STATUS (bidi_it, new_sidx - 1))))
            {
              while (bidi_it->level_stack[bidi_it->stack_idx].level
                     > current_level)
                {
-                 bidi_cache_iterator_state (bidi_it, type == NEUTRAL_B, 0);
+                 if (maxlevel < bidi_it->level_stack[bidi_it->stack_idx].level)
+                   maxlevel = bidi_it->level_stack[bidi_it->stack_idx].level;
+                 if (!bidi_cache_iterator_state (bidi_it,
+                                                 type == NEUTRAL_B, 0))
+                   {
+                     /* No more space in cache -- give up and let the
+                        opening bracket that started this be
+                        processed as any other NEUTRAL_ON.  */
+                     bidi_cache_reset_to (idx_at_entry - bidi_cache_start);
+                     bidi_copy_it (bidi_it, &saved_it);
+                     goto give_up;
+                   }
                  type = bidi_resolve_weak (bidi_it);
                }
            }
@@ -2498,11 +2679,11 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
              || (bidi_it->level_stack[bidi_it->stack_idx].level
                  != current_level))
            {
-           bpa_give_up:
              /* We've marched all the way to the end of this
                 isolating run sequence, and didn't find matching
                 closing brackets for some opening brackets.  Leave
                 their type unchanged.  */
+             pairing_pos = bidi_it->charpos;
              break;
            }
          if (bidi_it->type_after_wn == NEUTRAL_ON) /* Unicode 8.0 correction */
@@ -2513,13 +2694,85 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
 
       /* Restore bidi_it from the cache, which should have the bracket
         resolution members set as determined by the above loop.  */
-      type = bidi_cache_find (saved_it.charpos, 1, bidi_it);
+      type = bidi_cache_find (saved_it.charpos, 0, bidi_it);
       eassert (type == NEUTRAL_ON);
+
+      /* The following is an optimization for bracketed text that has
+        only one level which is equal to the paragraph's base
+        embedding level.  That is, only L2R and weak/neutral
+        characters in a L2R paragraph, or only R2L and weak/neutral
+        characters in a R2L paragraph.  Such brackets can be resolved
+        by bidi_resolve_neutral, which has a further shortcut for
+        this case.  So we pretend we did not resolve the brackets in
+        this case, set up next_for_neutral for the entire bracketed
+        text, and reset the cache to the character before the opening
+        bracket.  The upshot is to allow bidi_move_to_visually_next
+        reset the cache when it returns this opening bracket, thus
+        cutting significantly on the size of the cache, which is
+        important with long lines, especially if word-wrap is non-nil
+        (which requires the display engine to copy the cache back and
+        forth many times).  */
+      if (maxlevel == base_level
+         && ((base_level == 0 && !r2l_seen)
+             || (base_level == 1 && !l2r_seen)))
+       {
+         ptrdiff_t eob
+           = ((bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+              ? bidi_it->string.schars : ZV);
+
+         if (retval)
+           pairing_pos = bidi_it->bracket_pairing_pos;
+
+         /* This special value (which cannot possibly happen when
+            brackets are resolved, since there's no character at ZV)
+            will be noticed by bidi_resolve_explicit, and will be
+            copied to the following iterator states, instead of being
+            reset to -1.  */
+         bidi_it->bracket_pairing_pos = eob;
+         /* This type value will be used for resolving the outermost
+            closing bracket in bidi_resolve_brackets.  */
+         bidi_it->bracket_enclosed_type = embedding_type;
+         /* bidi_cache_last_idx is set to the index of the current
+            state, because we just called bidi_cache_find above.
+            That state describes the outermost opening bracket, the
+            one with which we entered this function.  Force the cache
+            to "forget" all the cached states starting from that state.  */
+         bidi_cache_reset_to (bidi_cache_last_idx - bidi_cache_start);
+         /* Set up the next_for_neutral member, to help
+            bidi_resolve_neutral.  */
+         bidi_it->next_for_neutral.type = embedding_type;
+         bidi_it->next_for_neutral.charpos = pairing_pos;
+         /* Pretend we didn't resolve this bracket.  */
+         retval = false;
+       }
     }
 
+ give_up:
   return retval;
 }
 
+static void
+bidi_record_type_for_neutral (struct bidi_saved_info *info, int level,
+                             bool nextp)
+{
+  int idx;
+
+  for (idx = bidi_cache_last_idx + 1; idx < bidi_cache_idx; idx++)
+    {
+      int lev = bidi_cache[idx].level_stack[bidi_cache[idx].stack_idx].level;
+
+      if (lev <= level)
+       {
+         eassert (lev == level);
+         if (nextp)
+           bidi_cache[idx].next_for_neutral = *info;
+         else
+           bidi_cache[idx].prev_for_neutral = *info;
+         break;
+       }
+    }
+}
+
 static bidi_type_t
 bidi_resolve_brackets (struct bidi_it *bidi_it)
 {
@@ -2527,52 +2780,93 @@ bidi_resolve_brackets (struct bidi_it *bidi_it)
   bool resolve_bracket = false;
   bidi_type_t type = UNKNOWN_BT;
   int ch;
-  struct bidi_saved_info tem_info;
+  struct bidi_saved_info prev_for_neutral, next_for_neutral;
+  ptrdiff_t eob
+    = ((bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+       ? bidi_it->string.schars : ZV);
 
-  bidi_remember_char (&tem_info, bidi_it, 1);
+  /* Record the prev_for_neutral type either from the previous
+     character, if it was a strong or AN/EN, or from the
+     prev_for_neutral information recorded previously.  */
+  if (bidi_it->type == STRONG_L || bidi_it->type == STRONG_R
+      || bidi_it->type == WEAK_AN || bidi_it->type == WEAK_EN)
+    bidi_remember_char (&prev_for_neutral, bidi_it, 1);
+  else
+    prev_for_neutral = bidi_it->prev_for_neutral;
+  /* Record the next_for_neutral type information.  */
+  if (bidi_it->next_for_neutral.charpos > bidi_it->charpos)
+    next_for_neutral = bidi_it->next_for_neutral;
+  else
+    next_for_neutral.charpos = -1;
   if (!bidi_it->first_elt)
     {
-      type = bidi_cache_find (bidi_it->charpos + bidi_it->nchars, 1, bidi_it);
+      type = bidi_cache_find (bidi_it->charpos + bidi_it->nchars, 0, bidi_it);
       ch = bidi_it->ch;
     }
   if (type == UNKNOWN_BT)
     {
       type = bidi_resolve_weak (bidi_it);
-      if (type == NEUTRAL_ON && bidi_find_bracket_pairs (bidi_it))
-       resolve_bracket = true;
+      if (type == NEUTRAL_ON)
+       {
+         /* bracket_pairing_pos == eob means this bracket does not
+            need to be resolved as a bracket, but as a neutral, see
+            the optimization trick we play near the end of
+            bidi_find_bracket_pairs.  */
+         if (bidi_it->bracket_pairing_pos == eob)
+           {
+             /* If this is the outermost closing bracket of a run of
+                characters in which we decided to resolve brackets as
+                neutrals, use the embedding level's type, recorded in
+                bracket_enclosed_type, to resolve the bracket.  */
+             if (bidi_it->next_for_neutral.charpos == bidi_it->charpos
+                 && bidi_paired_bracket_type (bidi_it->ch) == BIDI_BRACKET_CLOSE)
+               type = bidi_it->bracket_enclosed_type;
+           }
+         else if (bidi_find_bracket_pairs (bidi_it))
+           resolve_bracket = true;
+       }
     }
-  else
+  else if (bidi_it->bracket_pairing_pos != eob)
     {
+      eassert (bidi_it->resolved_level == -1);
+      /* If the cached state shows an increase of embedding level due
+        to an isolate initiator, we need to update the 1st cached
+        state of the next run of the current isolating sequence with
+        the prev_for_neutral and next_for_neutral information, so
+        that it will be picked up when we advance to that next run.  */
+      if (bidi_it->level_stack[bidi_it->stack_idx].level > prev_level
+         && ISOLATE_STATUS (bidi_it, bidi_it->stack_idx))
+       {
+         bidi_record_type_for_neutral (&prev_for_neutral, prev_level, 0);
+         bidi_record_type_for_neutral (&next_for_neutral, prev_level, 1);
+       }
       if (type == NEUTRAL_ON
          && bidi_paired_bracket_type (ch) == BIDI_BRACKET_OPEN)
        {
-         if (bidi_it->level_stack[bidi_it->stack_idx].level == prev_level)
+         if (bidi_it->bracket_pairing_pos > bidi_it->charpos)
            {
-             if (bidi_it->bracket_pairing_pos > 0)
-               {
-                 /* A cached opening bracket that wasn't completely
-                    resolved yet.  */
-                 resolve_bracket = true;
-               }
+             /* A cached opening bracket that wasn't completely
+                resolved yet.  */
+             resolve_bracket = true;
            }
-         else
+         else if (bidi_it->bracket_pairing_pos == -1)
            {
              /* Higher levels were not BPA-resolved yet, even if
-                cached by bidi_find_bracket_pairs.  Lower levels were
-                probably processed by bidi_find_bracket_pairs, but we
-                have no easy way of retaining the prev_for_neutral
-                from the previous level run of the isolating
-                sequence.  Force application of BPA now.  */
+                cached by bidi_find_bracket_pairs.  Force application
+                of BPA to the new level now.  */
              if (bidi_find_bracket_pairs (bidi_it))
                resolve_bracket = true;
            }
        }
-      /* Keep track of the prev_for_neutral type, needed for resolving
-        brackets below and for resolving neutrals in bidi_resolve_neutral.  */
-      if (bidi_it->level_stack[bidi_it->stack_idx].level == prev_level
-         && (tem_info.type == STRONG_L || tem_info.type == STRONG_R
-             || tem_info.type == WEAK_AN || tem_info.type == WEAK_EN))
-       bidi_it->prev_for_neutral = tem_info;
+      /* Keep track of the prev_for_neutral and next_for_neutral
+        types, needed for resolving brackets below and for resolving
+        neutrals in bidi_resolve_neutral.  */
+      if (bidi_it->level_stack[bidi_it->stack_idx].level == prev_level)
+       {
+         bidi_it->prev_for_neutral = prev_for_neutral;
+         if (next_for_neutral.charpos > 0)
+           bidi_it->next_for_neutral = next_for_neutral;
+       }
     }
 
   /* If needed, resolve the bracket type according to N0.  */
@@ -2657,9 +2951,18 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
       || (type == WEAK_BN && bidi_explicit_dir_char (bidi_it->ch)))
     {
       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);
+       {
+         /* Make sure the data for resolving neutrals we are
+            about to use is valid.  */
+         eassert (bidi_it->next_for_neutral.charpos > bidi_it->charpos
+                  /* PDI defines an eos, so it's OK for it to
+                     serve as its own next_for_neutral.  */
+                  || (bidi_it->next_for_neutral.charpos == bidi_it->charpos
+                      && bidi_it->type == PDI));
+         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
@@ -2722,14 +3025,14 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
            /* Skip level runs excluded from this isolating run sequence.  */
            new_sidx = bidi_it->stack_idx;
            if (bidi_it->level_stack[new_sidx].level > current_level
-               && (bidi_it->level_stack[new_sidx].isolate_status
+               && (ISOLATE_STATUS (bidi_it, new_sidx)
                    /* This is for when we have an isolate initiator
                       immediately followed by an embedding or
                       override initiator, in which case we get the
                       level stack pushed twice by the single call to
                       bidi_resolve_weak above.  */
                    || (new_sidx > old_sidx + 1
-                       && bidi_it->level_stack[new_sidx - 1].isolate_status)))
+                       && ISOLATE_STATUS (bidi_it, new_sidx - 1))))
              {
                while (bidi_it->level_stack[bidi_it->stack_idx].level
                       > current_level)
@@ -2855,16 +3158,13 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
        }
     }
 
-  /* Perhaps the character we want is already cached.  If it is, the
-     call to bidi_cache_find below will return a type other than
-     UNKNOWN_BT.  */
+  /* Perhaps the character we want is already cached s fully resolved.
+     If it is, the call to bidi_cache_find below will return a type
+     other than 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);
-      bidi_type_t prev_type = bidi_it->type;
-      bidi_type_t type_for_neutral = bidi_it->next_for_neutral.type;
-      ptrdiff_t pos_for_neutral = bidi_it->next_for_neutral.charpos;
 
       if (bidi_it->scan_dir > 0)
        {
@@ -2879,57 +3179,12 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
           cached at the beginning of the iteration.  */
        next_char_pos = bidi_it->charpos - 1;
       if (next_char_pos >= bob - 1)
-       type = bidi_cache_find (next_char_pos, 0, bidi_it);
-
-      /* For a sequence of BN and NI, copy the type from the previous
-        character.  This is because the loop in bidi_resolve_neutral
-        that handles such sequences caches the characters it
-        traverses, but does not (and cannot) store the
-        next_for_neutral member for them, because it is only known
-        when the loop ends.  So when we find them in the cache, their
-        type needs to be updated, but we don't have next_for_neutral
-        to do that.  However, whatever type is resolved as result of
-        that loop, it will be the same for all the traversed
-        characters, by virtue of N1 and N2.  */
-      if (type == WEAK_BN && bidi_it->scan_dir > 0
-         && bidi_explicit_dir_char (bidi_it->ch)
-         && type_for_neutral != UNKNOWN_BT
-         && bidi_it->charpos < pos_for_neutral)
-       {
-         type = prev_type;
-         eassert (type != UNKNOWN_BT);
-       }
+       type = bidi_cache_find (next_char_pos, 1, bidi_it);
       if (type != UNKNOWN_BT)
        {
-         /* If resolved_level is -1, it means this state was cached
-            before it was completely resolved, so we cannot return
-            it.  */
-         if (bidi_it->resolved_level != -1)
-           {
-             eassert (bidi_it->resolved_level >= 0);
-             return bidi_it->resolved_level;
-           }
-         else
-           {
-             level = bidi_it->level_stack[bidi_it->stack_idx].level;
-             if (bidi_get_category (type) == NEUTRAL
-                 || bidi_isolate_fmt_char (type))
-               {
-                 /* Make sure the data for resolving neutrals we are
-                    about to use is valid.  */
-                 if (bidi_it->next_for_neutral.charpos < bidi_it->charpos
-                     /* PDI defines an eos, so it's OK for it to
-                        serve as its own next_for_neutral.  */
-                     || (bidi_it->next_for_neutral.charpos == bidi_it->charpos
-                         && bidi_it->type != PDI)
-                     || bidi_it->next_for_neutral.type == UNKNOWN_BT)
-                   emacs_abort ();
-
-                 type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
-                                                bidi_it->next_for_neutral.type,
-                                                level);
-               }
-           }
+         /* We asked the cache for fully resolved states.  */
+         eassert (bidi_it->resolved_level >= 0);
+         return bidi_it->resolved_level;
        }
     }
 
@@ -3055,10 +3310,35 @@ bidi_find_other_level_edge (struct bidi_it *bidi_it, int level, bool end_flag)
       if (end_flag)
        emacs_abort ();
 
-      bidi_cache_iterator_state (bidi_it, 1, 0);
+      if (!bidi_cache_iterator_state (bidi_it, 1, 0))
+       {
+         /* Can't happen: if the cache needs to grow, it means we
+            were at base embedding level, so the cache should have
+            been either empty or already large enough to cover this
+            character position.  */
+         emacs_abort ();
+       }
       do {
        new_level = bidi_level_of_next_char (bidi_it);
-       bidi_cache_iterator_state (bidi_it, 1, 0);
+       /* If the cache is full, perform an emergency return by
+          pretending that the level ended.  */
+       if (!bidi_cache_iterator_state (bidi_it, 1, 0))
+         {
+           new_level = level - 1;
+           /* Since the cache should only grow when we are scanning
+              forward looking for the edge of the level that is one
+              above the base embedding level, we can only have this
+              contingency when LEVEL - 1 is the base embedding
+              level.  */
+           eassert (new_level == bidi_it->level_stack[0].level);
+           /* Plan B, for when the cache overflows: Back up to the
+              previous character by fetching the last cached state,
+              and force the resolved level of that character be the
+              base embedding level.  */
+           bidi_cache_fetch_state (bidi_cache_idx - 1, bidi_it);
+           bidi_it->resolved_level = new_level;
+           bidi_cache_iterator_state (bidi_it, 1, 1);
+         }
       } while (new_level >= level);
     }
 }
@@ -3212,6 +3492,12 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
          && bidi_it->charpos > (bidi_cache[bidi_cache_idx - 1].charpos
                                 + bidi_cache[bidi_cache_idx - 1].nchars - 1))
        bidi_cache_reset ();
+      /* Also reset the cache if it overflowed and we have just
+        emergency-exited using Plan B.  */
+      else if (bidi_it->resolved_level == bidi_it->level_stack[0].level
+              && bidi_cache_idx >= bidi_cache_size
+              && bidi_it->charpos == bidi_cache[bidi_cache_idx - 1].charpos)
+       bidi_cache_reset ();
        /* But as long as we are caching during forward scan, we must
           cache each state, or else the cache integrity will be
           compromised: it assumes cached states correspond to buffer
@@ -3227,6 +3513,33 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
     UNGCPRO;
 }
 
+/* Utility function for looking for strong directional characters
+   whose bidi type was overridden by a directional override.  */
+ptrdiff_t
+bidi_find_first_overridden (struct bidi_it *bidi_it)
+{
+  ptrdiff_t found_pos = ZV;
+
+  do
+    {
+      /* Need to call bidi_resolve_weak, not bidi_resolve_explicit,
+        because the directional overrides are applied by the
+        former.  */
+      bidi_type_t type = bidi_resolve_weak (bidi_it);
+
+      if ((type == STRONG_R && bidi_it->orig_type == STRONG_L)
+         || (type == STRONG_L
+             && (bidi_it->orig_type == STRONG_R
+                 || bidi_it->orig_type == STRONG_AL)))
+       found_pos = bidi_it->charpos;
+    } while (found_pos == ZV
+            && bidi_it->charpos < ZV
+            && bidi_it->ch != BIDI_EOB
+            && bidi_it->ch != '\n');
+
+  return found_pos;
+}
+
 /* This is meant to be called from within the debugger, whenever you
    wish to examine the cache contents.  */
 void bidi_dump_cached_states (void) EXTERNALLY_VISIBLE;