+/* Record one cached position found recently by
+ buf_charpos_to_bytepos or buf_bytepos_to_charpos. */
+
+static int cached_charpos;
+static int cached_bytepos;
+static struct buffer *cached_buffer;
+static int cached_modiff;
+
+/* Nonzero means enable debugging checks on byte/char correspondences. */
+
+static int byte_debug_flag;
+
+void
+clear_charpos_cache (b)
+ struct buffer *b;
+{
+ if (cached_buffer == b)
+ cached_buffer = 0;
+}
+\f
+/* Converting between character positions and byte positions. */
+
+/* There are several places in the buffer where we know
+ the corrspondence: BEG, BEGV, PT, GPT, ZV and Z,
+ and everywhere there is a marker. So we find the one of these places
+ that is closest to the specified position, and scan from there. */
+
+/* charpos_to_bytepos returns the byte position corresponding to CHARPOS. */
+
+/* This macro is a subroutine of charpos_to_bytepos.
+ Note that it is desirable that BYTEPOS is not evaluated
+ except when we really want its value. */
+
+#define CONSIDER(CHARPOS, BYTEPOS) \
+{ \
+ int this_charpos = (CHARPOS); \
+ int changed = 0; \
+ \
+ if (this_charpos == charpos) \
+ { \
+ int value = (BYTEPOS); \
+ if (byte_debug_flag) \
+ byte_char_debug_check (b, charpos, value); \
+ return value; \
+ } \
+ else if (this_charpos > charpos) \
+ { \
+ if (this_charpos < best_above) \
+ { \
+ best_above = this_charpos; \
+ best_above_byte = (BYTEPOS); \
+ changed = 1; \
+ } \
+ } \
+ else if (this_charpos > best_below) \
+ { \
+ best_below = this_charpos; \
+ best_below_byte = (BYTEPOS); \
+ changed = 1; \
+ } \
+ \
+ if (changed) \
+ { \
+ if (best_above - best_below == best_above_byte - best_below_byte) \
+ { \
+ int value = best_below_byte + (charpos - best_below); \
+ if (byte_debug_flag) \
+ byte_char_debug_check (b, charpos, value); \
+ return value; \
+ } \
+ } \
+}
+
+int
+byte_char_debug_check (b, charpos, bytepos)
+ struct buffer *b;
+ int charpos, bytepos;
+{
+ int nchars = 0;
+
+ if (bytepos > BUF_GPT_BYTE (b))
+ {
+ nchars = multibyte_chars_in_text (BUF_BEG_ADDR (b),
+ BUF_GPT_BYTE (b) - BUF_BEG_BYTE (b));
+ nchars += multibyte_chars_in_text (BUF_GAP_END_ADDR (b),
+ bytepos - BUF_GPT_BYTE (b));
+ }
+ else
+ nchars = multibyte_chars_in_text (BUF_BEG_ADDR (b),
+ bytepos - BUF_BEG_BYTE (b));
+
+ if (charpos - 1 != nchars)
+ abort ();
+}
+
+int
+charpos_to_bytepos (charpos)
+ int charpos;
+{
+ return buf_charpos_to_bytepos (current_buffer, charpos);
+}
+
+int
+buf_charpos_to_bytepos (b, charpos)
+ struct buffer *b;
+ int charpos;
+{
+ Lisp_Object tail;
+ int best_above, best_above_byte;
+ int best_below, best_below_byte;
+
+ if (charpos < BUF_BEG (b) || charpos > BUF_Z (b))
+ abort ();
+
+ best_above = BUF_Z (b);
+ best_above_byte = BUF_Z_BYTE (b);
+
+ /* If this buffer has as many characters as bytes,
+ each character must be one byte.
+ This takes care of the case where enable-multibyte-characters is nil. */
+ if (best_above == best_above_byte)
+ return charpos;
+
+ best_below = 1;
+ best_below_byte = 1;
+
+ /* We find in best_above and best_above_byte
+ the closest known point above CHARPOS,
+ and in best_below and best_below_byte
+ the closest known point below CHARPOS,
+
+ If at any point we can tell that the space between those
+ two best approximations is all single-byte,
+ we interpolate the result immediately. */
+
+ CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
+ CONSIDER (BUF_GPT (b), BUF_GPT_BYTE (b));
+ CONSIDER (BUF_BEGV (b), BUF_BEGV_BYTE (b));
+ CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));
+
+ if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
+ CONSIDER (cached_charpos, cached_bytepos);
+
+ tail = BUF_MARKERS (b);
+ while (XSYMBOL (tail) != XSYMBOL (Qnil))
+ {
+ CONSIDER (XMARKER (tail)->charpos, XMARKER (tail)->bytepos);
+
+ /* If we are down to a range of 50 chars,
+ don't bother checking any other markers;
+ scan the intervening chars directly now. */
+ if (best_above - best_below < 50)
+ break;
+
+ tail = XMARKER (tail)->chain;
+ }
+
+ /* We get here if we did not exactly hit one of the known places.
+ We have one known above and one known below.
+ Scan, counting characters, from whichever one is closer. */
+
+ if (charpos - best_below < best_above - charpos)
+ {
+ int record = charpos - best_below > 5000;
+
+ while (best_below != charpos)
+ {
+ best_below++;
+ BUF_INC_POS (b, best_below_byte);
+ }
+
+ /* If this position is quite far from the nearest known position,
+ cache the correspondence by creating a marker here.
+ It will last until the next GC. */
+ if (record)
+ {
+ Lisp_Object marker, buffer;
+ marker = Fmake_marker ();
+ XSETBUFFER (buffer, b);
+ set_marker_both (marker, buffer, best_below, best_below_byte);
+ }
+
+ if (byte_debug_flag)
+ byte_char_debug_check (b, charpos, best_below_byte);
+
+ cached_buffer = b;
+ cached_modiff = BUF_MODIFF (b);
+ cached_charpos = best_below;
+ cached_bytepos = best_below_byte;
+
+ return best_below_byte;
+ }
+ else
+ {
+ int record = best_above - charpos > 5000;
+
+ while (best_above != charpos)
+ {
+ best_above--;
+ BUF_DEC_POS (b, best_above_byte);
+ }
+
+ /* If this position is quite far from the nearest known position,
+ cache the correspondence by creating a marker here.
+ It will last until the next GC. */
+ if (record)
+ {
+ Lisp_Object marker, buffer;
+ marker = Fmake_marker ();
+ XSETBUFFER (buffer, b);
+ set_marker_both (marker, buffer, best_above, best_above_byte);
+ }
+
+ if (byte_debug_flag)
+ byte_char_debug_check (b, charpos, best_above_byte);
+
+ cached_buffer = b;
+ cached_modiff = BUF_MODIFF (b);
+ cached_charpos = best_above;
+ cached_bytepos = best_above_byte;
+
+ return best_above_byte;
+ }
+}
+
+#undef CONSIDER
+\f
+/* bytepos_to_charpos returns the char position corresponding to BYTEPOS. */
+
+/* This macro is a subroutine of bytepos_to_charpos.
+ It is used when BYTEPOS is actually the byte position. */
+
+#define CONSIDER(BYTEPOS, CHARPOS) \
+{ \
+ int this_bytepos = (BYTEPOS); \
+ int changed = 0; \
+ \
+ if (this_bytepos == bytepos) \
+ { \
+ int value = (CHARPOS); \
+ if (byte_debug_flag) \
+ byte_char_debug_check (b, value, bytepos); \
+ return value; \
+ } \
+ else if (this_bytepos > bytepos) \
+ { \
+ if (this_bytepos < best_above_byte) \
+ { \
+ best_above = (CHARPOS); \
+ best_above_byte = this_bytepos; \
+ changed = 1; \
+ } \
+ } \
+ else if (this_bytepos > best_below_byte) \
+ { \
+ best_below = (CHARPOS); \
+ best_below_byte = this_bytepos; \
+ changed = 1; \
+ } \
+ \
+ if (changed) \
+ { \
+ if (best_above - best_below == best_above_byte - best_below_byte) \
+ { \
+ int value = best_below + (bytepos - best_below_byte); \
+ if (byte_debug_flag) \
+ byte_char_debug_check (b, value, bytepos); \
+ return value; \
+ } \
+ } \
+}
+
+int
+bytepos_to_charpos (bytepos)
+ int bytepos;
+{
+ return buf_bytepos_to_charpos (current_buffer, bytepos);
+}
+
+int
+buf_bytepos_to_charpos (b, bytepos)
+ struct buffer *b;
+ int bytepos;
+{
+ Lisp_Object tail;
+ int best_above, best_above_byte;
+ int best_below, best_below_byte;
+
+ if (bytepos < BUF_BEG_BYTE (b) || bytepos > BUF_Z_BYTE (b))
+ abort ();
+
+ best_above = BUF_Z (b);
+ best_above_byte = BUF_Z_BYTE (b);
+
+ /* If this buffer has as many characters as bytes,
+ each character must be one byte.
+ This takes care of the case where enable-multibyte-characters is nil. */
+ if (best_above == best_above_byte)
+ return bytepos;
+
+ best_below = 1;
+ best_below_byte = 1;
+
+ CONSIDER (BUF_PT_BYTE (b), BUF_PT (b));
+ CONSIDER (BUF_GPT_BYTE (b), BUF_GPT (b));
+ CONSIDER (BUF_BEGV_BYTE (b), BUF_BEGV (b));
+ CONSIDER (BUF_ZV_BYTE (b), BUF_ZV (b));
+
+ if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
+ CONSIDER (cached_bytepos, cached_charpos);
+
+ tail = BUF_MARKERS (b);
+ while (XSYMBOL (tail) != XSYMBOL (Qnil))
+ {
+ CONSIDER (XMARKER (tail)->bytepos, XMARKER (tail)->charpos);
+
+ /* If we are down to a range of 50 chars,
+ don't bother checking any other markers;
+ scan the intervening chars directly now. */
+ if (best_above - best_below < 50)
+ break;
+
+ tail = XMARKER (tail)->chain;
+ }
+
+ /* We get here if we did not exactly hit one of the known places.
+ We have one known above and one known below.
+ Scan, counting characters, from whichever one is closer. */
+
+ if (bytepos - best_below_byte < best_above_byte - bytepos)
+ {
+ int record = bytepos - best_below_byte > 5000;
+
+ while (best_below_byte < bytepos)
+ {
+ best_below++;
+ BUF_INC_POS (b, best_below_byte);
+ }
+
+ /* If this position is quite far from the nearest known position,
+ cache the correspondence by creating a marker here.
+ It will last until the next GC.
+ But don't do it if BUF_MARKERS is nil;
+ that is a signal from Fset_buffer_multibyte. */
+ if (record && ! NILP (BUF_MARKERS (b)))
+ {
+ Lisp_Object marker, buffer;
+ marker = Fmake_marker ();
+ XSETBUFFER (buffer, b);
+ set_marker_both (marker, buffer, best_below, best_below_byte);
+ }
+
+ if (byte_debug_flag)
+ byte_char_debug_check (b, best_below, bytepos);
+
+ cached_buffer = b;
+ cached_modiff = BUF_MODIFF (b);
+ cached_charpos = best_below;
+ cached_bytepos = best_below_byte;
+
+ return best_below;
+ }
+ else
+ {
+ int record = best_above_byte - bytepos > 5000;
+
+ while (best_above_byte > bytepos)
+ {
+ best_above--;
+ BUF_DEC_POS (b, best_above_byte);
+ }
+
+ /* If this position is quite far from the nearest known position,
+ cache the correspondence by creating a marker here.
+ It will last until the next GC.
+ But don't do it if BUF_MARKERS is nil;
+ that is a signal from Fset_buffer_multibyte. */
+ if (record && ! NILP (BUF_MARKERS (b)))
+ {
+ Lisp_Object marker, buffer;
+ marker = Fmake_marker ();
+ XSETBUFFER (buffer, b);
+ set_marker_both (marker, buffer, best_above, best_above_byte);
+ }
+
+ if (byte_debug_flag)
+ byte_char_debug_check (b, best_above, bytepos);
+
+ cached_buffer = b;
+ cached_modiff = BUF_MODIFF (b);
+ cached_charpos = best_above;
+ cached_bytepos = best_above_byte;
+
+ return best_above;
+ }
+}
+
+#undef CONSIDER
+\f