2 Internal file viewer for the Midnight Commander
3 Function for work with coordinate cache (ccache)
5 Copyright (C) 1994-2024
6 Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
12 Joseph M. Hinkle, 1996
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009
17 Andrew Borodin <aborodin@vmail.ru>, 2009-2022
18 Ilia Maslakov <il.smind@gmail.com>, 2009
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software: you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation, either version 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program. If not, see <http://www.gnu.org/licenses/>.
37 This cache provides you with a fast lookup to map file offsets into
38 line/column pairs and vice versa. The interface to the mapping is
39 provided by the functions mcview_coord_to_offset() and
40 mcview_offset_to_coord().
42 The cache is implemented as a simple sorted array holding entries
43 that map some of the offsets to their line/column pair. Entries that
44 are not cached themselves are interpolated (exactly) from their
45 neighbor entries. The algorithm used for determining the line/column
46 for a specific offset needs to be kept synchronized with the one used
52 #include <string.h> /* memset() */
53 #ifdef MC_ENABLE_DEBUGGING_CODE
54 #include <inttypes.h> /* uintmax_t */
57 #include "lib/global.h"
58 #include "lib/tty/tty.h"
61 /*** global variables ****************************************************************************/
63 /*** file scope macro definitions ****************************************************************/
65 #define VIEW_COORD_CACHE_GRANUL 1024
66 #define CACHE_CAPACITY_DELTA 64
68 #define coord_cache_index(c, i) ((coord_cache_entry_t *) g_ptr_array_index ((c), (i)))
70 /*** file scope type declarations ****************************************************************/
72 typedef gboolean (*cmp_func_t
) (const coord_cache_entry_t
* a
, const coord_cache_entry_t
* b
);
74 /*** forward declarations (file scope functions) *************************************************/
76 /*** file scope variables ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
79 /*** file scope functions ************************************************************************/
80 /* --------------------------------------------------------------------------------------------- */
82 /* insert new cache entry into the cache */
84 mcview_ccache_add_entry (GPtrArray
*cache
, const coord_cache_entry_t
*entry
)
86 #if GLIB_CHECK_VERSION (2, 68, 0)
87 g_ptr_array_add (cache
, g_memdup2 (entry
, sizeof (*entry
)));
89 g_ptr_array_add (cache
, g_memdup (entry
, sizeof (*entry
)));
93 /* --------------------------------------------------------------------------------------------- */
96 mcview_coord_cache_entry_less_offset (const coord_cache_entry_t
*a
, const coord_cache_entry_t
*b
)
98 return (a
->cc_offset
< b
->cc_offset
);
101 /* --------------------------------------------------------------------------------------------- */
104 mcview_coord_cache_entry_less_plain (const coord_cache_entry_t
*a
, const coord_cache_entry_t
*b
)
106 if (a
->cc_line
< b
->cc_line
)
109 if (a
->cc_line
== b
->cc_line
)
110 return (a
->cc_column
< b
->cc_column
);
115 /* --------------------------------------------------------------------------------------------- */
118 mcview_coord_cache_entry_less_nroff (const coord_cache_entry_t
*a
, const coord_cache_entry_t
*b
)
120 if (a
->cc_line
< b
->cc_line
)
123 if (a
->cc_line
== b
->cc_line
)
124 return (a
->cc_nroff_column
< b
->cc_nroff_column
);
129 /* --------------------------------------------------------------------------------------------- */
130 /** Find and return the index of the last cache entry that is
131 * smaller than ''coord'', according to the criterion ''sort_by''. */
134 mcview_ccache_find (WView
*view
, const coord_cache_entry_t
*coord
, cmp_func_t cmp_func
)
137 size_t limit
= view
->coord_cache
->len
;
139 g_assert (limit
!= 0);
145 i
= base
+ limit
/ 2;
146 if (cmp_func (coord
, coord_cache_index (view
->coord_cache
, i
)))
148 /* continue the search in the lower half of the cache */
153 /* continue the search in the upper half of the cache */
157 limit
= (limit
+ 1) / 2;
163 /* --------------------------------------------------------------------------------------------- */
164 /*** public functions ****************************************************************************/
165 /* --------------------------------------------------------------------------------------------- */
167 #ifdef MC_ENABLE_DEBUGGING_CODE
170 mcview_ccache_dump (WView
*view
)
173 off_t offset
, line
, column
, nextline_offset
, filesize
;
175 const GPtrArray
*cache
= view
->coord_cache
;
177 g_assert (cache
!= NULL
);
179 filesize
= mcview_get_filesize (view
);
181 f
= fopen ("mcview-ccache.out", "w");
185 (void) setvbuf (f
, NULL
, _IONBF
, 0);
188 for (i
= 0; i
< cache
->len
; i
++)
190 coord_cache_entry_t
*e
;
192 e
= coord_cache_index (cache
, i
);
194 "entry %8u offset %8" PRIuMAX
195 " line %8" PRIuMAX
" column %8" PRIuMAX
196 " nroff_column %8" PRIuMAX
"\n",
198 (uintmax_t) e
->cc_offset
, (uintmax_t) e
->cc_line
, (uintmax_t) e
->cc_column
,
199 (uintmax_t) e
->cc_nroff_column
);
201 (void) fprintf (f
, "\n");
203 /* offset -> line/column translation */
204 for (offset
= 0; offset
< filesize
; offset
++)
206 mcview_offset_to_coord (view
, &line
, &column
, offset
);
208 "offset %8" PRIuMAX
" line %8" PRIuMAX
" column %8" PRIuMAX
"\n",
209 (uintmax_t) offset
, (uintmax_t) line
, (uintmax_t) column
);
212 /* line/column -> offset translation */
213 for (line
= 0; TRUE
; line
++)
215 mcview_coord_to_offset (view
, &nextline_offset
, line
+ 1, 0);
216 (void) fprintf (f
, "nextline_offset %8" PRIuMAX
"\n", (uintmax_t) nextline_offset
);
218 for (column
= 0; TRUE
; column
++)
220 mcview_coord_to_offset (view
, &offset
, line
, column
);
221 if (offset
>= nextline_offset
)
225 "line %8" PRIuMAX
" column %8" PRIuMAX
" offset %8" PRIuMAX
"\n",
226 (uintmax_t) line
, (uintmax_t) column
, (uintmax_t) offset
);
229 if (nextline_offset
>= filesize
- 1)
237 /* --------------------------------------------------------------------------------------------- */
238 /** Look up the missing components of ''coord'', which are given by
239 * ''lookup_what''. The function returns the smallest value that
240 * matches the existing components of ''coord''.
244 mcview_ccache_lookup (WView
*view
, coord_cache_entry_t
*coord
, enum ccache_type lookup_what
)
248 coord_cache_entry_t current
, next
, entry
;
249 enum ccache_type sorter
;
260 if (view
->coord_cache
== NULL
)
261 view
->coord_cache
= g_ptr_array_new_full (CACHE_CAPACITY_DELTA
, g_free
);
263 cache
= view
->coord_cache
;
267 memset (¤t
, 0, sizeof (current
));
268 mcview_ccache_add_entry (cache
, ¤t
);
271 sorter
= (lookup_what
== CCACHE_OFFSET
) ? CCACHE_LINECOL
: CCACHE_OFFSET
;
273 if (sorter
== CCACHE_OFFSET
)
274 cmp_func
= mcview_coord_cache_entry_less_offset
;
275 else if (view
->mode_flags
.nroff
)
276 cmp_func
= mcview_coord_cache_entry_less_nroff
;
278 cmp_func
= mcview_coord_cache_entry_less_plain
;
280 tty_enable_interrupt_key ();
283 /* find the two neighbor entries in the cache */
284 i
= mcview_ccache_find (view
, coord
, cmp_func
);
285 /* now i points to the lower neighbor in the cache */
287 current
= *coord_cache_index (cache
, i
);
288 if (i
+ 1 < view
->coord_cache
->len
)
289 limit
= coord_cache_index (cache
, i
+ 1)->cc_offset
;
291 limit
= current
.cc_offset
+ VIEW_COORD_CACHE_GRANUL
;
294 nroff_state
= NROFF_START
;
295 for (; current
.cc_offset
< limit
; current
= next
)
299 if (!mcview_get_byte (view
, current
.cc_offset
, &c
))
302 if (!cmp_func (¤t
, coord
) &&
303 (lookup_what
!= CCACHE_OFFSET
|| !view
->mode_flags
.nroff
|| nroff_state
== NROFF_START
))
306 /* Provide useful default values for 'next' */
307 next
.cc_offset
= current
.cc_offset
+ 1;
308 next
.cc_line
= current
.cc_line
;
309 next
.cc_column
= current
.cc_column
+ 1;
310 next
.cc_nroff_column
= current
.cc_nroff_column
+ 1;
312 /* and override some of them as necessary. */
317 mcview_get_byte_indexed (view
, current
.cc_offset
, 1, &nextc
);
319 /* Ignore '\r' if it is followed by '\r' or '\n'. If it is
320 * followed by anything else, it is a Mac line ending and
321 * produces a line break.
323 if (nextc
== '\r' || nextc
== '\n')
325 next
.cc_column
= current
.cc_column
;
326 next
.cc_nroff_column
= current
.cc_nroff_column
;
330 next
.cc_line
= current
.cc_line
+ 1;
332 next
.cc_nroff_column
= 0;
335 else if (nroff_state
== NROFF_BACKSPACE
)
336 next
.cc_nroff_column
= current
.cc_nroff_column
- 1;
339 next
.cc_column
= mcview_offset_rounddown (current
.cc_column
, 8) + 8;
340 next
.cc_nroff_column
= mcview_offset_rounddown (current
.cc_nroff_column
, 8) + 8;
344 next
.cc_line
= current
.cc_line
+ 1;
346 next
.cc_nroff_column
= 0;
350 ; /* Use all default values from above */
356 case NROFF_CONTINUATION
:
357 nroff_state
= mcview_is_nroff_sequence (view
, current
.cc_offset
)
358 ? NROFF_BACKSPACE
: NROFF_START
;
360 case NROFF_BACKSPACE
:
361 nroff_state
= NROFF_CONTINUATION
;
367 /* Cache entries must guarantee that for each i < j,
368 * line[i] <= line[j] and column[i] < column[j]. In the case of
369 * nroff sequences and '\r' characters, this is not guaranteed,
370 * so we cannot save them. */
371 if (nroff_state
== NROFF_START
&& c
!= '\r')
375 if (i
+ 1 == cache
->len
&& entry
.cc_offset
!= coord_cache_index (cache
, i
)->cc_offset
)
377 mcview_ccache_add_entry (cache
, &entry
);
379 if (!tty_got_interrupt ())
383 tty_disable_interrupt_key ();
385 if (lookup_what
== CCACHE_OFFSET
)
386 coord
->cc_offset
= current
.cc_offset
;
389 coord
->cc_line
= current
.cc_line
;
390 coord
->cc_column
= current
.cc_column
;
391 coord
->cc_nroff_column
= current
.cc_nroff_column
;
395 /* --------------------------------------------------------------------------------------------- */