src/layout.c: fixed missin case statement in set_display_type()
[free-mc.git] / src / viewer / coord_cache.c
blob211f0bd2edfb42c36865dc9c9614f06fede0cd3c
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for work with coordinate cache (ccache)
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
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 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 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, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
39 This cache provides you with a fast lookup to map file offsets into
40 line/column pairs and vice versa. The interface to the mapping is
41 provided by the functions mcview_coord_to_offset() and
42 mcview_offset_to_coord().
44 The cache is implemented as a simple sorted array holding entries
45 that map some of the offsets to their line/column pair. Entries that
46 are not cached themselves are interpolated (exactly) from their
47 neighbor entries. The algorithm used for determining the line/column
48 for a specific offset needs to be kept synchronized with the one used
49 in display().
52 #include <config.h>
54 #include <string.h> /* for g_memmove() */
56 #include "lib/global.h"
57 #include "lib/tty/tty.h"
58 #include "internal.h"
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
64 #define VIEW_COORD_CACHE_GRANUL 1024
65 #define CACHE_CAPACITY_DELTA 64
67 /*** file scope type declarations ****************************************************************/
69 typedef gboolean (*cmp_func_t) (const coord_cache_entry_t * a, const coord_cache_entry_t * b);
71 /*** file scope variables ************************************************************************/
73 /*** file scope functions ************************************************************************/
75 /* --------------------------------------------------------------------------------------------- */
77 /* insert new cache entry into the cache */
78 static void
79 mcview_ccache_add_entry (coord_cache_t * cache, size_t pos, const coord_cache_entry_t * entry)
81 if ((cache == NULL) || (entry == NULL))
82 return;
84 pos = min (pos, cache->size);
86 /* increase cache capacity if needed */
87 if (cache->size == cache->capacity)
89 cache->capacity += CACHE_CAPACITY_DELTA;
90 cache->cache = g_realloc (cache->cache, cache->capacity * sizeof (coord_cache_entry_t *));
93 /* insert new entry */
94 if (pos != cache->size)
95 g_memmove (cache->cache[pos + 1], cache->cache[pos],
96 (cache->size - pos) * sizeof (coord_cache_entry_t *));
97 cache->cache[pos] = g_memdup (entry, sizeof (coord_cache_entry_t));
98 cache->size++;
101 static gboolean
102 mcview_coord_cache_entry_less_offset (const coord_cache_entry_t * a, const coord_cache_entry_t * b)
104 return (a->cc_offset < b->cc_offset);
107 static gboolean
108 mcview_coord_cache_entry_less_plain (const coord_cache_entry_t * a, const coord_cache_entry_t * b)
110 if (a->cc_line < b->cc_line)
111 return TRUE;
113 if (a->cc_line == b->cc_line)
114 return (a->cc_column < b->cc_column);
116 return FALSE;
120 static gboolean
121 mcview_coord_cache_entry_less_nroff (const coord_cache_entry_t * a, const coord_cache_entry_t * b)
123 if (a->cc_line < b->cc_line)
124 return TRUE;
126 if (a->cc_line == b->cc_line)
127 return (a->cc_nroff_column < b->cc_nroff_column);
129 return FALSE;
133 /* Find and return the index of the last cache entry that is
134 * smaller than ''coord'', according to the criterion ''sort_by''. */
135 static inline size_t
136 mcview_ccache_find (mcview_t * view, const coord_cache_entry_t * coord, cmp_func_t cmp_func)
138 size_t base = 0;
139 size_t limit = view->coord_cache->size;
141 assert (limit != 0);
143 while (limit > 1)
145 size_t i;
147 i = base + limit / 2;
148 if (cmp_func (coord, view->coord_cache->cache[i]))
150 /* continue the search in the lower half of the cache */
152 else
154 /* continue the search in the upper half of the cache */
155 base = i;
157 limit = (limit + 1) / 2;
159 return base;
162 /* --------------------------------------------------------------------------------------------- */
164 /*** public functions ****************************************************************************/
166 /* --------------------------------------------------------------------------------------------- */
168 coord_cache_t *
169 coord_cache_new (void)
171 coord_cache_t *cache;
173 cache = g_new (coord_cache_t, 1);
174 cache->size = 0;
175 cache->capacity = CACHE_CAPACITY_DELTA;
176 cache->cache = g_malloc0 (cache->capacity * sizeof (coord_cache_entry_t *));
178 return cache;
181 /* --------------------------------------------------------------------------------------------- */
183 void
184 coord_cache_free (coord_cache_t * cache)
186 if (cache != NULL)
188 size_t i;
190 for (i = 0; i < cache->size; i++)
191 g_free (cache->cache[i]);
193 g_free (cache->cache);
194 g_free (cache);
198 /* --------------------------------------------------------------------------------------------- */
200 #ifdef MC_ENABLE_DEBUGGING_CODE
202 void
203 mcview_ccache_dump (mcview_t * view)
205 FILE *f;
206 off_t offset, line, column, nextline_offset, filesize;
207 guint i;
208 const coord_cache_t *cache = view->coord_cache;
210 assert (cache != NULL);
212 filesize = mcview_get_filesize (view);
214 f = fopen ("mcview-ccache.out", "w");
215 if (f == NULL)
216 return;
217 (void) setvbuf (f, NULL, _IONBF, 0);
219 /* cache entries */
220 for (i = 0; i < view->coord_cache->size; i++)
222 (void) fprintf (f,
223 "entry %8u "
224 "offset %8" OFFSETTYPE_PRId " "
225 "line %8" OFFSETTYPE_PRId " "
226 "column %8" OFFSETTYPE_PRId " "
227 "nroff_column %8" OFFSETTYPE_PRId "\n",
228 (unsigned int) i, cache->cache[i].cc_offset, cache[i]->cache.cc_line,
229 cache->cache[i].cc_column, cache->cache[i].cc_nroff_column);
231 (void) fprintf (f, "\n");
233 /* offset -> line/column translation */
234 for (offset = 0; offset < filesize; offset++)
236 mcview_offset_to_coord (view, &line, &column, offset);
237 (void) fprintf (f,
238 "offset %8" OFFSETTYPE_PRId " "
239 "line %8" OFFSETTYPE_PRId " "
240 "column %8" OFFSETTYPE_PRId "\n", offset, line, column);
243 /* line/column -> offset translation */
244 for (line = 0; TRUE; line++)
246 mcview_coord_to_offset (view, &nextline_offset, line + 1, 0);
247 (void) fprintf (f, "nextline_offset %8" OFFSETTYPE_PRId "\n", nextline_offset);
249 for (column = 0; TRUE; column++)
251 mcview_coord_to_offset (view, &offset, line, column);
252 if (offset >= nextline_offset)
253 break;
255 (void) fprintf (f,
256 "line %8" OFFSETTYPE_PRId " column %8" OFFSETTYPE_PRId " offset %8"
257 OFFSETTYPE_PRId "\n", line, column, offset);
260 if (nextline_offset >= filesize - 1)
261 break;
264 (void) fclose (f);
266 #endif
268 /* --------------------------------------------------------------------------------------------- */
271 /* Look up the missing components of ''coord'', which are given by
272 * ''lookup_what''. The function returns the smallest value that
273 * matches the existing components of ''coord''.
275 void
276 mcview_ccache_lookup (mcview_t * view, coord_cache_entry_t * coord, enum ccache_type lookup_what)
278 size_t i;
279 coord_cache_t *cache;
280 coord_cache_entry_t current, next, entry;
281 enum ccache_type sorter;
282 off_t limit;
283 cmp_func_t cmp_func;
285 enum
287 NROFF_START,
288 NROFF_BACKSPACE,
289 NROFF_CONTINUATION
290 } nroff_state;
292 if (view->coord_cache == NULL)
293 view->coord_cache = coord_cache_new ();
295 cache = view->coord_cache;
297 if (cache->size == 0)
299 current.cc_offset = 0;
300 current.cc_line = 0;
301 current.cc_column = 0;
302 current.cc_nroff_column = 0;
303 mcview_ccache_add_entry (cache, 0, &current);
306 sorter = (lookup_what == CCACHE_OFFSET) ? CCACHE_LINECOL : CCACHE_OFFSET;
308 if (sorter == CCACHE_OFFSET)
309 cmp_func = mcview_coord_cache_entry_less_offset;
310 else if (view->text_nroff_mode)
311 cmp_func = mcview_coord_cache_entry_less_nroff;
312 else
313 cmp_func = mcview_coord_cache_entry_less_plain;
316 tty_enable_interrupt_key ();
318 retry:
319 /* find the two neighbor entries in the cache */
320 i = mcview_ccache_find (view, coord, cmp_func);
321 /* now i points to the lower neighbor in the cache */
323 current = *cache->cache[i];
324 if (i + 1 < view->coord_cache->size)
325 limit = cache->cache[i + 1]->cc_offset;
326 else
327 limit = current.cc_offset + VIEW_COORD_CACHE_GRANUL;
329 entry = current;
330 nroff_state = NROFF_START;
331 for (; current.cc_offset < limit; current = next)
333 int c, nextc;
335 if (!mcview_get_byte (view, current.cc_offset, &c))
336 break;
338 if (!cmp_func (&current, coord))
340 if (lookup_what == CCACHE_OFFSET && view->text_nroff_mode && nroff_state != NROFF_START)
342 /* don't break here */
344 else
346 break;
350 /* Provide useful default values for ''next'' */
351 next.cc_offset = current.cc_offset + 1;
352 next.cc_line = current.cc_line;
353 next.cc_column = current.cc_column + 1;
354 next.cc_nroff_column = current.cc_nroff_column + 1;
356 /* and override some of them as necessary. */
357 if (c == '\r')
359 mcview_get_byte_indexed (view, current.cc_offset, 1, &nextc);
361 /* Ignore '\r' if it is followed by '\r' or '\n'. If it is
362 * followed by anything else, it is a Mac line ending and
363 * produces a line break.
365 if (nextc == '\r' || nextc == '\n')
367 next.cc_column = current.cc_column;
368 next.cc_nroff_column = current.cc_nroff_column;
370 else
372 next.cc_line = current.cc_line + 1;
373 next.cc_column = 0;
374 next.cc_nroff_column = 0;
378 else if (nroff_state == NROFF_BACKSPACE)
380 next.cc_nroff_column = current.cc_nroff_column - 1;
383 else if (c == '\t')
385 next.cc_column = mcview_offset_rounddown (current.cc_column, 8) + 8;
386 next.cc_nroff_column = mcview_offset_rounddown (current.cc_nroff_column, 8) + 8;
389 else if (c == '\n')
391 next.cc_line = current.cc_line + 1;
392 next.cc_column = 0;
393 next.cc_nroff_column = 0;
396 else
398 /* Use all default values from above */
401 switch (nroff_state)
403 case NROFF_START:
404 case NROFF_CONTINUATION:
405 nroff_state = mcview_is_nroff_sequence (view, current.cc_offset)
406 ? NROFF_BACKSPACE : NROFF_START;
407 break;
408 case NROFF_BACKSPACE:
409 nroff_state = NROFF_CONTINUATION;
410 break;
413 /* Cache entries must guarantee that for each i < j,
414 * line[i] <= line[j] and column[i] < column[j]. In the case of
415 * nroff sequences and '\r' characters, this is not guaranteed,
416 * so we cannot save them. */
417 if (nroff_state == NROFF_START && c != '\r')
418 entry = next;
421 if (i + 1 == cache->size && entry.cc_offset != cache->cache[i]->cc_offset)
423 mcview_ccache_add_entry (cache, cache->size, &entry);
425 if (!tty_got_interrupt ())
426 goto retry;
429 tty_disable_interrupt_key ();
431 if (lookup_what == CCACHE_OFFSET)
433 coord->cc_offset = current.cc_offset;
435 else
437 coord->cc_line = current.cc_line;
438 coord->cc_column = current.cc_column;
439 coord->cc_nroff_column = current.cc_nroff_column;
443 /* --------------------------------------------------------------------------------------------- */