]> code.delx.au - gnu-emacs/blobdiff - src/bidi.c
Merge from origin/emacs-24
[gnu-emacs] / src / bidi.c
index d03aa4e3e103d5ea338facc6e0eae8b6470daf4d..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.
@@ -76,6 +76,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
      bidi_fetch_char         -- fetch next character
      bidi_resolve_explicit   -- resolve explicit levels and directions
      bidi_resolve_weak       -- resolve weak types
+     bidi_resolve_brackets   -- resolve "paired brackets" neutral types
      bidi_resolve_neutral    -- resolve neutral types
      bidi_level_of_next_char -- resolve implicit levels
 
@@ -261,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
 /***********************************************************************
@@ -432,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
@@ -446,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).  */
@@ -472,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;
@@ -481,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
@@ -489,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,
@@ -541,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) };
@@ -561,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
@@ -573,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
@@ -590,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
@@ -608,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)
 {
@@ -682,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;
@@ -720,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)
 {
@@ -748,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)
     {
@@ -757,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
     {
@@ -788,36 +847,43 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, bool resolved,
       bidi_cache[idx].next_for_ws = bidi_it->next_for_ws;
       bidi_cache[idx].disp_pos = bidi_it->disp_pos;
       bidi_cache[idx].disp_prop = bidi_it->disp_prop;
-      bidi_cache[idx].bracket_resolved = bidi_it->bracket_resolved;
+      bidi_cache[idx].bracket_pairing_pos = bidi_it->bracket_pairing_pos;
+      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;
 
@@ -835,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;
 }
 
@@ -853,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);
@@ -889,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;
@@ -928,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;
 }
@@ -949,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 ();
        }
     }
@@ -988,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));
@@ -1019,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]*$");
@@ -1034,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;
 }
@@ -1078,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;
@@ -1096,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 */
@@ -1107,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 */
@@ -1711,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
@@ -1734,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 */
@@ -1743,7 +1839,6 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
         correction to N0, as implemented in bidi_resolve_weak/W1
         below.  */
       if (bidi_it->type_after_wn == NEUTRAL_ON
-         && bidi_it->bracket_resolved
          && bidi_get_category (bidi_it->type) == STRONG
          && bidi_paired_bracket_type (bidi_it->ch) == BIDI_BRACKET_CLOSE)
        bidi_remember_char (&bidi_it->prev, bidi_it, 1);
@@ -1761,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)
     {
@@ -1769,8 +1871,14 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
       bidi_it->next_en_type = UNKNOWN_BT;
     }
 
-  /* Reset the bracket_resolved flag.  */
-  bidi_it->bracket_resolved = 0;
+  /* 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
@@ -1801,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.  */
@@ -1834,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))
@@ -2009,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);
@@ -2017,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;
@@ -2065,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
@@ -2324,23 +2435,56 @@ bidi_resolve_neutral_1 (bidi_type_t prev_type, bidi_type_t next_type, int lev)
 
 #define FLAG_EMBEDDING_INSIDE  1
 #define FLAG_OPPOSITE_INSIDE   2
-#define FLAG_EMBEDDING_OUTSIDE 4
-#define FLAG_OPPOSITE_OUTSIDE  8
 
 /* A data type used in the stack maintained by
-   bidi_resolve_bracket_pairs below.  */
+   bidi_find_bracket_pairs below.  */
 typedef struct bpa_stack_entry {
   int close_bracket_char;
   int open_bracket_idx;
 #ifdef ENABLE_CHECKING
   ptrdiff_t open_bracket_pos;
 #endif
-  unsigned flags : 4;
+  unsigned flags : 2;
 } 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 \
@@ -2349,35 +2493,34 @@ typedef struct bpa_stack_entry {
 # define STORE_BRACKET_CHARPOS /* nothing */
 #endif
 
-#define PUSH_BPA_STACK(EMBEDDING_LEVEL, LAST_STRONG) \
+#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;           \
-   STORE_BRACKET_CHARPOS;                                              \
-   if (((EMBEDDING_LEVEL) & 1) == 0)                                   \
-     bpa_stack[bpa_sp].flags = ((LAST_STRONG) == STRONG_L              \
-                               ? FLAG_EMBEDDING_OUTSIDE                \
-                               : FLAG_OPPOSITE_OUTSIDE);               \
-   else                                                                        \
-     bpa_stack[bpa_sp].flags = ((LAST_STRONG) == STRONG_L              \
-                               ? FLAG_OPPOSITE_OUTSIDE                 \
-                               : FLAG_EMBEDDING_OUTSIDE);              \
+    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)
 
 
 /* This function implements BPA, the Bidi Parenthesis Algorithm,
-   described in BD16 and N0 of UAX#9.  */
-static bidi_type_t
-bidi_resolve_bracket_pairs (struct bidi_it *bidi_it)
+   described in BD16 and N0 of UAX#9.  It finds all the bracket pairs
+   in the current isolating sequence, and records the enclosed type
+   and the position of the matching bracket in the cache.  It returns
+   non-zero if called with the iterator on the opening bracket which
+   has a matching closing bracket in the current isolating sequence,
+   zero otherwise.  */
+static bool
+bidi_find_bracket_pairs (struct bidi_it *bidi_it)
 {
   bidi_bracket_type_t btype;
   bidi_type_t type = bidi_it->type;
+  bool retval = false;
 
   /* When scanning backwards, we don't expect any unresolved bidi
      bracket characters.  */
@@ -2390,13 +2533,17 @@ bidi_resolve_bracket_pairs (struct bidi_it *bidi_it)
       bpa_stack_entry bpa_stack[MAX_BPA_STACK];
       int bpa_sp = -1;
       struct bidi_it saved_it;
-      bidi_type_t last_strong;
+      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);
-      last_strong = bidi_it->prev_for_neutral.type;
       /* bidi_cache_iterator_state refuses to cache on backward scans,
         and bidi_cache_fetch_state doesn't bring scan_dir from the
         cache, so we must initialize this explicitly.  */
@@ -2407,59 +2554,82 @@ bidi_resolve_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 (embedding_level, last_strong);
+           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)
                sp--;
              if (sp >= 0)
                {
-                 /* Resolve the bracket type according to N0.  */
-                 if (bpa_stack[sp].flags & FLAG_EMBEDDING_INSIDE) /* N0b */
-                   type = ((embedding_level & 1) ? STRONG_R : STRONG_L);
-                 else if ((bpa_stack[sp].flags /* N0c1 */
-                           & (FLAG_OPPOSITE_INSIDE | FLAG_OPPOSITE_OUTSIDE))
-                          == (FLAG_OPPOSITE_INSIDE | FLAG_OPPOSITE_OUTSIDE))
-                   type = ((embedding_level & 1) ? STRONG_L : STRONG_R);
-                 else if (bpa_stack[sp].flags & FLAG_OPPOSITE_INSIDE) /*N0c2*/
-                   type = ((embedding_level & 1) ? STRONG_R : STRONG_L);
-
-                 /* Update the type of the closing bracket.  */
-                 bidi_it->type = type;
                  /* Update and cache the corresponding opening bracket.  */
                  bidi_cache_fetch_state (bpa_stack[sp].open_bracket_idx,
                                          &tem_it);
 #ifdef ENABLE_CHECKING
                  eassert (bpa_stack[sp].open_bracket_pos == tem_it.charpos);
 #endif
-                 tem_it.type = type;
-                 bidi_cache_iterator_state (&tem_it, 0, 0);
+                 /* Determine the enclosed type for this bracket
+                    pair's type resolution according to N0.  */
+                 if (bpa_stack[sp].flags & FLAG_EMBEDDING_INSIDE)
+                   tem_it.bracket_enclosed_type = embedding_type; /* N0b */
+                 else if (bpa_stack[sp].flags & FLAG_OPPOSITE_INSIDE)
+                   tem_it.bracket_enclosed_type                   /* N0c */
+                     = (embedding_type == STRONG_L ? STRONG_R : STRONG_L);
+                 else                                             /* N0d */
+                   tem_it.bracket_enclosed_type = UNKNOWN_BT;
+
+                 /* Record the position of the matching closing
+                    bracket, and update the cache.  */
+                 tem_it.bracket_pairing_pos = bidi_it->charpos;
+                 bidi_cache_iterator_state (&tem_it, 0, 1);
+
+                 /* Pop the BPA stack.  */
                  bpa_sp = sp - 1;
                }
-             bidi_it->bracket_resolved = 1;
-             bidi_cache_iterator_state (bidi_it, 0, 0);
              if (bpa_sp < 0)
-               break;
+               {
+                 retval = true;
+                 break;
+               }
            }
          else if (bidi_get_category (bidi_it->type_after_wn) != NEUTRAL)
            {
-             unsigned flag;
+             unsigned flag = 0;
              int sp;
 
-             /* Update the "inside" flags of all the slots on the stack.  */
+             /* Whenever we see a strong type, update the flags of
+                all the slots on the stack.  */
              switch (bidi_it->type)
                {
                case STRONG_L:
                  flag = ((embedding_level & 1) == 0
                          ? FLAG_EMBEDDING_INSIDE
                          : FLAG_OPPOSITE_INSIDE);
-                 last_strong = STRONG_L;
+                 l2r_seen = true;
                  break;
                case STRONG_R:
                case WEAK_EN:
@@ -2467,30 +2637,41 @@ bidi_resolve_bracket_pairs (struct bidi_it *bidi_it)
                  flag = ((embedding_level & 1) == 1
                          ? FLAG_EMBEDDING_INSIDE
                          : FLAG_OPPOSITE_INSIDE);
-                 last_strong = STRONG_R;
+                 r2l_seen = true;
                  break;
                default:
                  break;
                }
-             for (sp = bpa_sp; sp >= 0; sp--)
-               bpa_stack[sp].flags |= flag;
-             /* FIXME: Pay attention to types that can be
-                next_for_neutral, and when found, update cached
-                states for which it is relevant.  */
+             if (flag)
+               {
+                 for (sp = bpa_sp; sp >= 0; sp--)
+                   bpa_stack[sp].flags |= flag;
+               }
            }
          old_sidx = bidi_it->stack_idx;
          type = bidi_resolve_weak (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,26 +2679,11 @@ bidi_resolve_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.  Unwind
-                whatever is left on the BPA stack, and mark each
-                bracket there as BPA-resolved, leaving their type
-                unchanged.  */
-             while (bpa_sp >= 0)
-               {
-                 bidi_cache_fetch_state (bpa_stack[bpa_sp].open_bracket_idx,
-                                         &tem_it);
-#ifdef ENABLE_CHECKING
-                 eassert (bpa_stack[bpa_sp].open_bracket_pos
-                          == tem_it.charpos);
-#endif
-                 tem_it.bracket_resolved = 1;
-                 bidi_cache_iterator_state (&tem_it, 0, 0);
-                 bpa_sp--;
-               }
-             type = saved_it.type;
+                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 */
@@ -2525,31 +2691,230 @@ bidi_resolve_bracket_pairs (struct bidi_it *bidi_it)
          else
            btype = BIDI_BRACKET_NONE;
        }
-      bidi_check_type (type);
 
-      bidi_copy_it (bidi_it, &saved_it);
-      bidi_it->type = type;
-      bidi_it->bracket_resolved = 1;
+      /* 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, 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;
+       }
     }
 
-  return type;
+ 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)
 {
-  bidi_type_t type = bidi_resolve_weak (bidi_it);
-  int ch = bidi_it->ch;
+  int prev_level = bidi_it->level_stack[bidi_it->stack_idx].level;
+  bool resolve_bracket = false;
+  bidi_type_t type = UNKNOWN_BT;
+  int ch;
+  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);
 
-  if (type == NEUTRAL_ON
-      && bidi_paired_bracket_type (ch) != BIDI_BRACKET_NONE)
+  /* 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)
     {
-      if (bidi_cache_idx > bidi_cache_start
-         && bidi_cache_find (bidi_it->charpos, 1, bidi_it) != UNKNOWN_BT
-         && bidi_it->bracket_resolved)
-       type = bidi_it->type;
+      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)
+       {
+         /* 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 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->bracket_pairing_pos > bidi_it->charpos)
+           {
+             /* A cached opening bracket that wasn't completely
+                resolved yet.  */
+             resolve_bracket = true;
+           }
+         else if (bidi_it->bracket_pairing_pos == -1)
+           {
+             /* Higher levels were not BPA-resolved yet, even if
+                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 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.  */
+  if (resolve_bracket)
+    {
+      int embedding_level = bidi_it->level_stack[bidi_it->stack_idx].level;
+      bidi_type_t embedding_type = (embedding_level & 1) ? STRONG_R : STRONG_L;
+
+      eassert (bidi_it->prev_for_neutral.type != UNKNOWN_BT);
+      eassert (bidi_it->bracket_pairing_pos > bidi_it->charpos);
+      if (bidi_it->bracket_enclosed_type == embedding_type) /* N0b */
+       type = embedding_type;
       else
-       type = bidi_resolve_bracket_pairs (bidi_it);
+       {
+         switch (bidi_it->prev_for_neutral.type)
+           {
+           case STRONG_R:
+           case WEAK_EN:
+           case WEAK_AN:
+             type =
+               (bidi_it->bracket_enclosed_type == STRONG_R) /* N0c */
+               ? STRONG_R                                   /* N0c1 */
+               : embedding_type;                            /* N0c2 */
+             break;
+           case STRONG_L:
+             type =
+               (bidi_it->bracket_enclosed_type == STRONG_L) /* N0c */
+               ? STRONG_L                                   /* N0c1 */
+               : embedding_type;                            /* N0c2 */
+             break;
+           default:
+             /* N0d: Do not set the type for that bracket pair.  */
+             break;
+           }
+       }
+      eassert (type == STRONG_L || type == STRONG_R || type == NEUTRAL_ON);
+
+      /* Update the type of the paired closing bracket to the same
+        type as for the resolved opening bracket.  */
+      if (type != NEUTRAL_ON)
+       {
+         ptrdiff_t idx = bidi_cache_search (bidi_it->bracket_pairing_pos,
+                                            -1, 1);
+
+         if (idx < bidi_cache_start)
+           emacs_abort ();
+         bidi_cache[idx].type = type;
+       }
     }
 
   return type;
@@ -2558,21 +2923,10 @@ bidi_resolve_brackets (struct bidi_it *bidi_it)
 static bidi_type_t
 bidi_resolve_neutral (struct bidi_it *bidi_it)
 {
-  bool string_p = bidi_it->string.s || STRINGP (bidi_it->string.lstring);
-  int prev_level = bidi_it->level_stack[bidi_it->stack_idx].level;
-  bidi_type_t type = UNKNOWN_BT;
+  bidi_type_t type = bidi_resolve_brackets (bidi_it);
   int current_level;
   bool is_neutral;
 
-  if (bidi_cache_idx > bidi_cache_start && !bidi_it->first_elt)
-    {
-      if (bidi_it->nchars <= 0)
-       emacs_abort ();
-      type = bidi_cache_find (bidi_it->charpos + bidi_it->nchars, 1, bidi_it);
-    }
-  if (type == UNKNOWN_BT)
-    type = bidi_resolve_brackets (bidi_it);
-
   eassert (type == STRONG_R
           || type == STRONG_L
           || type == WEAK_BN
@@ -2586,7 +2940,6 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
           || type == RLI
           || type == PDI);
 
-  eassert (prev_level >= 0);
   current_level = bidi_it->level_stack[bidi_it->stack_idx].level;
   eassert (current_level >= 0);
   is_neutral = bidi_get_category (type) == NEUTRAL;
@@ -2598,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
@@ -2663,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)
@@ -2796,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)
        {
@@ -2820,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;
        }
     }
 
@@ -2996,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);
     }
 }
@@ -3153,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
@@ -3161,10 +3506,40 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
        bidi_cache_iterator_state (bidi_it, 1, 0);
     }
 
+  eassert (bidi_it->resolved_level >= 0
+          && bidi_it->resolved_level <= BIDI_MAXDEPTH + 2);
+
   if (STRINGP (bidi_it->string.lstring))
     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;