xref: /freebsd/contrib/ncurses/ncurses/tty/hashmap.c (revision 5ca44d1c)
10e3d5408SPeter Wemm /****************************************************************************
25ca44d1cSRong-En Fan  * Copyright 2019,2020 Thomas E. Dickey                                     *
30e3d5408SPeter Wemm  * Copyright 1998-2015,2016 Free Software Foundation, Inc.                  *
40e3d5408SPeter Wemm  *                                                                          *
50e3d5408SPeter Wemm  * Permission is hereby granted, free of charge, to any person obtaining a  *
60e3d5408SPeter Wemm  * copy of this software and associated documentation files (the            *
70e3d5408SPeter Wemm  * "Software"), to deal in the Software without restriction, including      *
80e3d5408SPeter Wemm  * without limitation the rights to use, copy, modify, merge, publish,      *
90e3d5408SPeter Wemm  * distribute, distribute with modifications, sublicense, and/or sell       *
100e3d5408SPeter Wemm  * copies of the Software, and to permit persons to whom the Software is    *
110e3d5408SPeter Wemm  * furnished to do so, subject to the following conditions:                 *
120e3d5408SPeter Wemm  *                                                                          *
130e3d5408SPeter Wemm  * The above copyright notice and this permission notice shall be included  *
140e3d5408SPeter Wemm  * in all copies or substantial portions of the Software.                   *
150e3d5408SPeter Wemm  *                                                                          *
160e3d5408SPeter Wemm  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
170e3d5408SPeter Wemm  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
180e3d5408SPeter Wemm  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
190e3d5408SPeter Wemm  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
200e3d5408SPeter Wemm  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
210e3d5408SPeter Wemm  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
220e3d5408SPeter Wemm  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
230e3d5408SPeter Wemm  *                                                                          *
240e3d5408SPeter Wemm  * Except as contained in this notice, the name(s) of the above copyright   *
250e3d5408SPeter Wemm  * holders shall not be used in advertising or otherwise to promote the     *
260e3d5408SPeter Wemm  * sale, use or other dealings in this Software without prior written       *
270e3d5408SPeter Wemm  * authorization.                                                           *
280e3d5408SPeter Wemm  ****************************************************************************/
290e3d5408SPeter Wemm 
300e3d5408SPeter Wemm /****************************************************************************
310e3d5408SPeter Wemm  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
320e3d5408SPeter Wemm  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
330e3d5408SPeter Wemm  ****************************************************************************/
340e3d5408SPeter Wemm 
350e3d5408SPeter Wemm /******************************************************************************
360e3d5408SPeter Wemm 
370e3d5408SPeter Wemm NAME
380e3d5408SPeter Wemm    hashmap.c -- fill in scramble vector based on text hashes
390e3d5408SPeter Wemm 
400e3d5408SPeter Wemm SYNOPSIS
410e3d5408SPeter Wemm    void _nc_hash_map(void)
420e3d5408SPeter Wemm 
430e3d5408SPeter Wemm DESCRIPTION:
440e3d5408SPeter Wemm    This code attempts to recognize pairs of old and new lines in the physical
450e3d5408SPeter Wemm and virtual screens.  When a line pair is recognized, the old line index is
460e3d5408SPeter Wemm placed in the oldindex member of the virtual screen line, to be used by the
470e3d5408SPeter Wemm vertical-motion optimizer portion of the update logic (see hardscroll.c).
480e3d5408SPeter Wemm 
490e3d5408SPeter Wemm    Line pairs are recognized by applying a modified Heckel's algorithm,
500e3d5408SPeter Wemm sped up by hashing.  If a line hash is unique in both screens, those
510e3d5408SPeter Wemm lines must be a pair. Then if the lines just before or after the pair
520e3d5408SPeter Wemm are the same or similar, they are a pair too.
530e3d5408SPeter Wemm 
540e3d5408SPeter Wemm    We don't worry about false pairs produced by hash collisions, on the
550e3d5408SPeter Wemm assumption that such cases are rare and will only make the latter stages
560e3d5408SPeter Wemm of update less efficient, not introduce errors.
570e3d5408SPeter Wemm 
580e3d5408SPeter Wemm HOW TO TEST THIS:
590e3d5408SPeter Wemm 
600e3d5408SPeter Wemm Use the following production:
610e3d5408SPeter Wemm 
620e3d5408SPeter Wemm hashmap: hashmap.c
630e3d5408SPeter Wemm 	$(CC) -g -DHASHDEBUG hashmap.c hardscroll.c ../objects/lib_trace.o -o hashmap
640e3d5408SPeter Wemm 
650e3d5408SPeter Wemm AUTHOR
660e3d5408SPeter Wemm     Eric S. Raymond <esr@snark.thyrsus.com>, May 1996
670e3d5408SPeter Wemm     Bug fixes and improvements by Alexander V. Lukyanov <lav@yars.free.net>, 1997
680e3d5408SPeter Wemm 
690e3d5408SPeter Wemm *****************************************************************************/
700e3d5408SPeter Wemm 
710e3d5408SPeter Wemm #include <curses.priv.h>
720e3d5408SPeter Wemm 
735ca44d1cSRong-En Fan #ifndef CUR
740e3d5408SPeter Wemm #define CUR SP_TERMTYPE
750e3d5408SPeter Wemm #endif
760e3d5408SPeter Wemm 
770e3d5408SPeter Wemm MODULE_ID("$Id: hashmap.c,v 1.69 2020/05/31 17:50:48 tom Exp $")
780e3d5408SPeter Wemm 
790e3d5408SPeter Wemm #ifdef HASHDEBUG
800e3d5408SPeter Wemm 
810e3d5408SPeter Wemm # define _tracef	printf
820e3d5408SPeter Wemm # undef TR
830e3d5408SPeter Wemm # ifdef TRACE
845ca44d1cSRong-En Fan # define TR(n, a)	if (_nc_tracing & (n)) { _tracef a ; putchar('\n'); }
855ca44d1cSRong-En Fan # else
860e3d5408SPeter Wemm # define TR(n, a)	{ _tracef a ; putchar('\n'); }
870e3d5408SPeter Wemm # endif
880e3d5408SPeter Wemm # undef screen_lines
890e3d5408SPeter Wemm # define screen_lines(sp) MAXLINES
900e3d5408SPeter Wemm # define TEXTWIDTH(sp)	1
910e3d5408SPeter Wemm static int oldnums[MAXLINES], reallines[MAXLINES];
920e3d5408SPeter Wemm static NCURSES_CH_T oldtext[MAXLINES][TEXTWIDTH(sp)];
935ca44d1cSRong-En Fan static NCURSES_CH_T newtext[MAXLINES][TEXTWIDTH(sp)];
940e3d5408SPeter Wemm # define OLDNUM(sp,n)	oldnums[n]
950e3d5408SPeter Wemm # define OLDTEXT(sp,n)	oldtext[n]
960e3d5408SPeter Wemm # define NEWTEXT(sp,m)	newtext[m]
970e3d5408SPeter Wemm # define PENDING(sp,n)  1
980e3d5408SPeter Wemm 
990e3d5408SPeter Wemm #else /* !HASHDEBUG */
1000e3d5408SPeter Wemm 
1010e3d5408SPeter Wemm # define OLDNUM(sp,n)	(sp)->_oldnum_list[n]
1020e3d5408SPeter Wemm # define OLDTEXT(sp,n)	CurScreen(sp)->_line[n].text
10339f2269fSPeter Wemm # define NEWTEXT(sp,m)	NewScreen(sp)->_line[m].text
10439f2269fSPeter Wemm # define TEXTWIDTH(sp)	(CurScreen(sp)->_maxx + 1)
10539f2269fSPeter Wemm # define PENDING(sp,n)  (NewScreen(sp)->_line[n].firstchar != _NOCHANGE)
10639f2269fSPeter Wemm 
10739f2269fSPeter Wemm #endif /* !HASHDEBUG */
10839f2269fSPeter Wemm 
10939f2269fSPeter Wemm #define oldhash(sp)	((sp)->oldhash)
11039f2269fSPeter Wemm #define newhash(sp)	((sp)->newhash)
1110e3d5408SPeter Wemm #define hashtab(sp)	((sp)->hashtab)
1124a1a9510SRong-En Fan #define lines_alloc(sp)	((sp)->hashtab_len)
1134a1a9510SRong-En Fan 
1144a1a9510SRong-En Fan #if USE_WIDEC_SUPPORT
11539f2269fSPeter Wemm #define HASH_VAL(ch) (ch.chars[0])
1160e3d5408SPeter Wemm #else
1170e3d5408SPeter Wemm #define HASH_VAL(ch) (ch)
11839f2269fSPeter Wemm #endif
1190e3d5408SPeter Wemm 
1207a69bbfbSPeter Wemm static const NCURSES_CH_T blankchar = NewChar(BLANK_TEXT);
1210e3d5408SPeter Wemm 
12239f2269fSPeter Wemm static NCURSES_INLINE unsigned long
hash(SCREEN * sp,NCURSES_CH_T * text)1230e3d5408SPeter Wemm hash(SCREEN *sp, NCURSES_CH_T *text)
1240e3d5408SPeter Wemm {
1250e3d5408SPeter Wemm     int i;
1260e3d5408SPeter Wemm     NCURSES_CH_T ch;
1270e3d5408SPeter Wemm     unsigned long result = 0;
1287a69bbfbSPeter Wemm     (void) sp;
12939f2269fSPeter Wemm 
1300e3d5408SPeter Wemm     for (i = TEXTWIDTH(sp); i > 0; i--) {
1310e3d5408SPeter Wemm 	ch = *text++;
1320e3d5408SPeter Wemm 	result += (result << 5) + (unsigned long) HASH_VAL(ch);
1330e3d5408SPeter Wemm     }
1345ca44d1cSRong-En Fan     return result;
1355ca44d1cSRong-En Fan }
1360e3d5408SPeter Wemm 
1370e3d5408SPeter Wemm /* approximate update cost */
1380e3d5408SPeter Wemm static int
update_cost(SCREEN * sp,NCURSES_CH_T * from,NCURSES_CH_T * to)1390e3d5408SPeter Wemm update_cost(SCREEN *sp, NCURSES_CH_T *from, NCURSES_CH_T *to)
14039f2269fSPeter Wemm {
1417a69bbfbSPeter Wemm     int cost = 0;
14239f2269fSPeter Wemm     int i;
1430e3d5408SPeter Wemm     (void) sp;
1440e3d5408SPeter Wemm 
1450e3d5408SPeter Wemm     for (i = TEXTWIDTH(sp); i > 0; i--, from++, to++)
1464a1a9510SRong-En Fan 	if (!(CharEq(*from, *to)))
1470e3d5408SPeter Wemm 	    cost++;
1480e3d5408SPeter Wemm 
1494a1a9510SRong-En Fan     return cost;
1500e3d5408SPeter Wemm }
1515ca44d1cSRong-En Fan 
1525ca44d1cSRong-En Fan static int
update_cost_from_blank(SCREEN * sp,NCURSES_CH_T * to)1530e3d5408SPeter Wemm update_cost_from_blank(SCREEN *sp, NCURSES_CH_T *to)
1540e3d5408SPeter Wemm {
1550e3d5408SPeter Wemm     int cost = 0;
1560e3d5408SPeter Wemm     int i;
1570e3d5408SPeter Wemm     NCURSES_CH_T blank = blankchar;
1580e3d5408SPeter Wemm     (void) sp;
1590e3d5408SPeter Wemm 
1600e3d5408SPeter Wemm     if (back_color_erase)
1610e3d5408SPeter Wemm 	SetPair(blank, GetPair(stdscr->_nc_bkgd));
1624a1a9510SRong-En Fan 
1637a69bbfbSPeter Wemm     for (i = TEXTWIDTH(sp); i > 0; i--, to++)
1640e3d5408SPeter Wemm 	if (!(CharEq(blank, *to)))
1650e3d5408SPeter Wemm 	    cost++;
1660e3d5408SPeter Wemm 
1670e3d5408SPeter Wemm     return cost;
1680e3d5408SPeter Wemm }
1690e3d5408SPeter Wemm 
1700e3d5408SPeter Wemm /*
1710e3d5408SPeter Wemm  * Returns true when moving line 'from' to line 'to' seems to be cost
1720e3d5408SPeter Wemm  * effective. 'blank' indicates whether the line 'to' would become blank.
1730e3d5408SPeter Wemm  */
1740e3d5408SPeter Wemm static NCURSES_INLINE bool
cost_effective(SCREEN * sp,const int from,const int to,const int blank)1750e3d5408SPeter Wemm cost_effective(SCREEN *sp, const int from, const int to, const int blank)
1760e3d5408SPeter Wemm {
1770e3d5408SPeter Wemm     int new_from;
1780e3d5408SPeter Wemm 
1790e3d5408SPeter Wemm     if (from == to)
1800e3d5408SPeter Wemm 	return FALSE;
1810e3d5408SPeter Wemm 
1820e3d5408SPeter Wemm     new_from = OLDNUM(sp, from);
1830e3d5408SPeter Wemm     if (new_from == _NEWINDEX)
1840e3d5408SPeter Wemm 	new_from = from;
1850e3d5408SPeter Wemm 
1867a69bbfbSPeter Wemm     /*
1877a69bbfbSPeter Wemm      * On the left side of >= is the cost before moving;
1880e3d5408SPeter Wemm      * on the right side -- cost after moving.
1890e3d5408SPeter Wemm      */
1900e3d5408SPeter Wemm     return (((blank ? update_cost_from_blank(sp, NEWTEXT(sp, to))
1910e3d5408SPeter Wemm 	      : update_cost(sp, OLDTEXT(sp, to), NEWTEXT(sp, to)))
1920e3d5408SPeter Wemm 	     + update_cost(sp, OLDTEXT(sp, new_from), NEWTEXT(sp, from)))
1930e3d5408SPeter Wemm 	    >= ((new_from == from ? update_cost_from_blank(sp, NEWTEXT(sp, from))
1940e3d5408SPeter Wemm 		 : update_cost(sp, OLDTEXT(sp, new_from), NEWTEXT(sp, from)))
1950e3d5408SPeter Wemm 		+ update_cost(sp, OLDTEXT(sp, from), NEWTEXT(sp, to))))
1960e3d5408SPeter Wemm 	? TRUE : FALSE;
1970e3d5408SPeter Wemm }
1980e3d5408SPeter Wemm 
1990e3d5408SPeter Wemm static void
grow_hunks(SCREEN * sp)2000e3d5408SPeter Wemm grow_hunks(SCREEN *sp)
2010e3d5408SPeter Wemm {
2020e3d5408SPeter Wemm     int back_limit;		/* limits for cells to fill */
2030e3d5408SPeter Wemm     int back_ref_limit;		/* limit for references */
2040e3d5408SPeter Wemm     int i;
2057a69bbfbSPeter Wemm     int next_hunk;
2060e3d5408SPeter Wemm 
2070e3d5408SPeter Wemm     /*
2080e3d5408SPeter Wemm      * This is tricky part.  We have unique pairs to use as anchors.
2090e3d5408SPeter Wemm      * Use these to deduce the presence of spans of identical lines.
2100e3d5408SPeter Wemm      */
2117a69bbfbSPeter Wemm     back_limit = 0;
2127a69bbfbSPeter Wemm     back_ref_limit = 0;
2130e3d5408SPeter Wemm 
2140e3d5408SPeter Wemm     i = 0;
2150e3d5408SPeter Wemm     while (i < screen_lines(sp) && OLDNUM(sp, i) == _NEWINDEX)
2160e3d5408SPeter Wemm 	i++;
2170e3d5408SPeter Wemm     for (; i < screen_lines(sp); i = next_hunk) {
2180e3d5408SPeter Wemm 	int forward_limit;
2190e3d5408SPeter Wemm 	int forward_ref_limit;
2200e3d5408SPeter Wemm 	int end;
2210e3d5408SPeter Wemm 	int start = i;
2220e3d5408SPeter Wemm 	int shift = OLDNUM(sp, i) - i;
2230e3d5408SPeter Wemm 
2240e3d5408SPeter Wemm 	/* get forward limit */
2250e3d5408SPeter Wemm 	i = start + 1;
2260e3d5408SPeter Wemm 	while (i < screen_lines(sp)
2270e3d5408SPeter Wemm 	       && OLDNUM(sp, i) != _NEWINDEX
2287a69bbfbSPeter Wemm 	       && OLDNUM(sp, i) - i == shift)
2290e3d5408SPeter Wemm 	    i++;
2307a69bbfbSPeter Wemm 	end = i;
2310e3d5408SPeter Wemm 	while (i < screen_lines(sp) && OLDNUM(sp, i) == _NEWINDEX)
2320e3d5408SPeter Wemm 	    i++;
2330e3d5408SPeter Wemm 	next_hunk = i;
2340e3d5408SPeter Wemm 	forward_limit = i;
2357a69bbfbSPeter Wemm 	if (i >= screen_lines(sp) || OLDNUM(sp, i) >= i)
2360e3d5408SPeter Wemm 	    forward_ref_limit = i;
2370e3d5408SPeter Wemm 	else
2380e3d5408SPeter Wemm 	    forward_ref_limit = OLDNUM(sp, i);
2390e3d5408SPeter Wemm 
2400e3d5408SPeter Wemm 	i = start - 1;
2410e3d5408SPeter Wemm 	/* grow back */
2420e3d5408SPeter Wemm 	if (shift < 0)
2430e3d5408SPeter Wemm 	    back_limit = back_ref_limit + (-shift);
2440e3d5408SPeter Wemm 	while (i >= back_limit) {
2450e3d5408SPeter Wemm 	    if (newhash(sp)[i] == oldhash(sp)[i + shift]
2460e3d5408SPeter Wemm 		|| cost_effective(sp, i + shift, i, shift < 0)) {
2470e3d5408SPeter Wemm 		OLDNUM(sp, i) = i + shift;
2487a69bbfbSPeter Wemm 		TR(TRACE_UPDATE | TRACE_MOVE,
2490e3d5408SPeter Wemm 		   ("connected new line %d to old line %d (backward continuation)",
2507a69bbfbSPeter Wemm 		    i, i + shift));
2510e3d5408SPeter Wemm 	    } else {
2520e3d5408SPeter Wemm 		TR(TRACE_UPDATE | TRACE_MOVE,
2530e3d5408SPeter Wemm 		   ("not connecting new line %d to old line %d (backward continuation)",
2540e3d5408SPeter Wemm 		    i, i + shift));
2557a69bbfbSPeter Wemm 		break;
2560e3d5408SPeter Wemm 	    }
2570e3d5408SPeter Wemm 	    i--;
2580e3d5408SPeter Wemm 	}
2590e3d5408SPeter Wemm 
2600e3d5408SPeter Wemm 	i = end;
2610e3d5408SPeter Wemm 	/* grow forward */
2620e3d5408SPeter Wemm 	if (shift > 0)
2630e3d5408SPeter Wemm 	    forward_limit = forward_ref_limit - shift;
2640e3d5408SPeter Wemm 	while (i < forward_limit) {
2650e3d5408SPeter Wemm 	    if (newhash(sp)[i] == oldhash(sp)[i + shift]
2660e3d5408SPeter Wemm 		|| cost_effective(sp, i + shift, i, shift > 0)) {
2670e3d5408SPeter Wemm 		OLDNUM(sp, i) = i + shift;
2680e3d5408SPeter Wemm 		TR(TRACE_UPDATE | TRACE_MOVE,
2690e3d5408SPeter Wemm 		   ("connected new line %d to old line %d (forward continuation)",
2707a69bbfbSPeter Wemm 		    i, i + shift));
2717a69bbfbSPeter Wemm 	    } else {
2720e3d5408SPeter Wemm 		TR(TRACE_UPDATE | TRACE_MOVE,
27339f2269fSPeter Wemm 		   ("not connecting new line %d to old line %d (forward continuation)",
2740e3d5408SPeter Wemm 		    i, i + shift));
2750e3d5408SPeter Wemm 		break;
2760e3d5408SPeter Wemm 	    }
2777a69bbfbSPeter Wemm 	    i++;
2780e3d5408SPeter Wemm 	}
2790e3d5408SPeter Wemm 
28039f2269fSPeter Wemm 	back_ref_limit = back_limit = i;
2817a69bbfbSPeter Wemm 	if (shift > 0)
2827a69bbfbSPeter Wemm 	    back_ref_limit += shift;
2830e3d5408SPeter Wemm     }
28415589c42SPeter Wemm }
2850e3d5408SPeter Wemm 
2860e3d5408SPeter Wemm NCURSES_EXPORT(void)
NCURSES_SP_NAME(_nc_hash_map)2870e3d5408SPeter Wemm NCURSES_SP_NAME(_nc_hash_map) (NCURSES_SP_DCL0)
2880e3d5408SPeter Wemm {
2890e3d5408SPeter Wemm     HASHMAP *hsp;
2900e3d5408SPeter Wemm     register int i;
2917a69bbfbSPeter Wemm 
2920e3d5408SPeter Wemm     if (screen_lines(SP_PARM) > lines_alloc(SP_PARM)) {
2937a69bbfbSPeter Wemm 	if (hashtab(SP_PARM))
2940e3d5408SPeter Wemm 	    free(hashtab(SP_PARM));
2950e3d5408SPeter Wemm 	hashtab(SP_PARM) = typeMalloc(HASHMAP,
2960e3d5408SPeter Wemm 				      ((size_t) screen_lines(SP_PARM) + 1) * 2);
2977a69bbfbSPeter Wemm 	if (!hashtab(SP_PARM)) {
2980e3d5408SPeter Wemm 	    if (oldhash(SP_PARM)) {
2990e3d5408SPeter Wemm 		FreeAndNull(oldhash(SP_PARM));
3004a1a9510SRong-En Fan 	    }
3010e3d5408SPeter Wemm 	    lines_alloc(SP_PARM) = 0;
3024a1a9510SRong-En Fan 	    return;
3030e3d5408SPeter Wemm 	}
3040e3d5408SPeter Wemm 	lines_alloc(SP_PARM) = screen_lines(SP_PARM);
3057a69bbfbSPeter Wemm     }
3060e3d5408SPeter Wemm 
3070e3d5408SPeter Wemm     if (oldhash(SP_PARM) && newhash(SP_PARM)) {
3080e3d5408SPeter Wemm 	/* re-hash only changed lines */
3090e3d5408SPeter Wemm 	for (i = 0; i < screen_lines(SP_PARM); i++) {
3100e3d5408SPeter Wemm 	    if (PENDING(SP_PARM, i))
3110e3d5408SPeter Wemm 		newhash(SP_PARM)[i] = hash(SP_PARM, NEWTEXT(SP_PARM, i));
3127a69bbfbSPeter Wemm 	}
3130e3d5408SPeter Wemm     } else {
3140e3d5408SPeter Wemm 	/* re-hash all */
3150e3d5408SPeter Wemm 	if (oldhash(SP_PARM) == 0)
3160e3d5408SPeter Wemm 	    oldhash(SP_PARM) = typeCalloc(unsigned long,
3170e3d5408SPeter Wemm 					    (size_t) screen_lines(SP_PARM));
3180e3d5408SPeter Wemm 	if (newhash(SP_PARM) == 0)
3190e3d5408SPeter Wemm 	    newhash(SP_PARM) = typeCalloc(unsigned long,
3200e3d5408SPeter Wemm 					    (size_t) screen_lines(SP_PARM));
3210e3d5408SPeter Wemm 	if (!oldhash(SP_PARM) || !newhash(SP_PARM))
3220e3d5408SPeter Wemm 	    return;		/* malloc failure */
3230e3d5408SPeter Wemm 	for (i = 0; i < screen_lines(SP_PARM); i++) {
3247a69bbfbSPeter Wemm 	    newhash(SP_PARM)[i] = hash(SP_PARM, NEWTEXT(SP_PARM, i));
3250e3d5408SPeter Wemm 	    oldhash(SP_PARM)[i] = hash(SP_PARM, OLDTEXT(SP_PARM, i));
3260e3d5408SPeter Wemm 	}
3270e3d5408SPeter Wemm     }
3280e3d5408SPeter Wemm 
3290e3d5408SPeter Wemm #ifdef HASH_VERIFY
3300e3d5408SPeter Wemm     for (i = 0; i < screen_lines(SP_PARM); i++) {
3310e3d5408SPeter Wemm 	if (newhash(SP_PARM)[i] != hash(SP_PARM, NEWTEXT(SP_PARM, i)))
3320e3d5408SPeter Wemm 	    fprintf(stderr, "error in newhash[%d]\n", i);
3330e3d5408SPeter Wemm 	if (oldhash(SP_PARM)[i] != hash(SP_PARM, OLDTEXT(SP_PARM, i)))
3347a69bbfbSPeter Wemm 	    fprintf(stderr, "error in oldhash[%d]\n", i);
3350e3d5408SPeter Wemm     }
3360e3d5408SPeter Wemm #endif
3370e3d5408SPeter Wemm 
3380e3d5408SPeter Wemm     /*
3390e3d5408SPeter Wemm      * Set up and count line-hash values.
3400e3d5408SPeter Wemm      */
3410e3d5408SPeter Wemm     memset(hashtab(SP_PARM), '\0',
3420e3d5408SPeter Wemm 	   sizeof(*(hashtab(SP_PARM)))
3430e3d5408SPeter Wemm 	   * ((size_t) screen_lines(SP_PARM) + 1) * 2);
3440e3d5408SPeter Wemm     for (i = 0; i < screen_lines(SP_PARM); i++) {
3450e3d5408SPeter Wemm 	unsigned long hashval = oldhash(SP_PARM)[i];
3460e3d5408SPeter Wemm 
3470e3d5408SPeter Wemm 	for (hsp = hashtab(SP_PARM); hsp->hashval; hsp++)
3480e3d5408SPeter Wemm 	    if (hsp->hashval == hashval)
3490e3d5408SPeter Wemm 		break;
3500e3d5408SPeter Wemm 	hsp->hashval = hashval;	/* in case this is a new entry */
3510e3d5408SPeter Wemm 	hsp->oldcount++;
3520e3d5408SPeter Wemm 	hsp->oldindex = i;
3530e3d5408SPeter Wemm     }
3540e3d5408SPeter Wemm     for (i = 0; i < screen_lines(SP_PARM); i++) {
3550e3d5408SPeter Wemm 	unsigned long hashval = newhash(SP_PARM)[i];
3567a69bbfbSPeter Wemm 
3570e3d5408SPeter Wemm 	for (hsp = hashtab(SP_PARM); hsp->hashval; hsp++)
3580e3d5408SPeter Wemm 	    if (hsp->hashval == hashval)
3590e3d5408SPeter Wemm 		break;
3600e3d5408SPeter Wemm 	hsp->hashval = hashval;	/* in case this is a new entry */
3610e3d5408SPeter Wemm 	hsp->newcount++;
3620e3d5408SPeter Wemm 	hsp->newindex = i;
3630e3d5408SPeter Wemm 
3640e3d5408SPeter Wemm 	OLDNUM(SP_PARM, i) = _NEWINDEX;		/* initialize old indices array */
3650e3d5408SPeter Wemm     }
3660e3d5408SPeter Wemm 
3670e3d5408SPeter Wemm     /*
3680e3d5408SPeter Wemm      * Mark line pairs corresponding to unique hash pairs.
3690e3d5408SPeter Wemm      *
3700e3d5408SPeter Wemm      * We don't mark lines with offset 0, because it can make fail
3717a69bbfbSPeter Wemm      * extending hunks by cost_effective. Otherwise, it does not
3720e3d5408SPeter Wemm      * have any side effects.
3730e3d5408SPeter Wemm      */
3740e3d5408SPeter Wemm     for (hsp = hashtab(SP_PARM); hsp->hashval; hsp++)
3750e3d5408SPeter Wemm 	if (hsp->oldcount == 1 && hsp->newcount == 1
3760e3d5408SPeter Wemm 	    && hsp->oldindex != hsp->newindex) {
3770e3d5408SPeter Wemm 	    TR(TRACE_UPDATE | TRACE_MOVE,
3780e3d5408SPeter Wemm 	       ("new line %d is hash-identical to old line %d (unique)",
3797a69bbfbSPeter Wemm 		hsp->newindex, hsp->oldindex));
3807a69bbfbSPeter Wemm 	    OLDNUM(SP_PARM, hsp->newindex) = hsp->oldindex;
3810e3d5408SPeter Wemm 	}
3820e3d5408SPeter Wemm 
3837a69bbfbSPeter Wemm     grow_hunks(SP_PARM);
3847a69bbfbSPeter Wemm 
3850e3d5408SPeter Wemm     /*
3860e3d5408SPeter Wemm      * Eliminate bad or impossible shifts -- this includes removing
3870e3d5408SPeter Wemm      * those hunks which could not grow because of conflicts, as well
3880e3d5408SPeter Wemm      * those which are to be moved too far, they are likely to destroy
3890e3d5408SPeter Wemm      * more than carry.
3900e3d5408SPeter Wemm      */
3910e3d5408SPeter Wemm     for (i = 0; i < screen_lines(SP_PARM);) {
3920e3d5408SPeter Wemm 	int start, shift, size;
3930e3d5408SPeter Wemm 
3940e3d5408SPeter Wemm 	while (i < screen_lines(SP_PARM) && OLDNUM(SP_PARM, i) == _NEWINDEX)
3957a69bbfbSPeter Wemm 	    i++;
3967a69bbfbSPeter Wemm 	if (i >= screen_lines(SP_PARM))
3970e3d5408SPeter Wemm 	    break;
3980e3d5408SPeter Wemm 	start = i;
3990e3d5408SPeter Wemm 	shift = OLDNUM(SP_PARM, i) - i;
4000e3d5408SPeter Wemm 	i++;
4010e3d5408SPeter Wemm 	while (i < screen_lines(SP_PARM)
4027a69bbfbSPeter Wemm 	       && OLDNUM(SP_PARM, i) != _NEWINDEX
4037a69bbfbSPeter Wemm 	       && OLDNUM(SP_PARM, i) - i == shift)
4040e3d5408SPeter Wemm 	    i++;
40539f2269fSPeter Wemm 	size = i - start;
4060e3d5408SPeter Wemm 	if (size < 3 || size + min(size / 8, 2) < abs(shift)) {
4070e3d5408SPeter Wemm 	    while (start < i) {
4080e3d5408SPeter Wemm 		OLDNUM(SP_PARM, start) = _NEWINDEX;
4090e3d5408SPeter Wemm 		start++;
4100e3d5408SPeter Wemm 	    }
4110e3d5408SPeter Wemm 	}
4127a69bbfbSPeter Wemm     }
4130e3d5408SPeter Wemm 
4140e3d5408SPeter Wemm     /* After clearing invalid hunks, try grow the rest. */
4150e3d5408SPeter Wemm     grow_hunks(SP_PARM);
4167a69bbfbSPeter Wemm }
4170e3d5408SPeter Wemm 
4180e3d5408SPeter Wemm #if NCURSES_SP_FUNCS
4190e3d5408SPeter Wemm NCURSES_EXPORT(void)
_nc_hash_map(void)4200e3d5408SPeter Wemm _nc_hash_map(void)
4210e3d5408SPeter Wemm {
4220e3d5408SPeter Wemm     NCURSES_SP_NAME(_nc_hash_map) (CURRENT_SCREEN);
4230e3d5408SPeter Wemm }
4240e3d5408SPeter Wemm #endif
4250e3d5408SPeter Wemm 
4260e3d5408SPeter Wemm NCURSES_EXPORT(void)
NCURSES_SP_NAME(_nc_make_oldhash)4277a69bbfbSPeter Wemm NCURSES_SP_NAME(_nc_make_oldhash) (NCURSES_SP_DCLx int i)
4287a69bbfbSPeter Wemm {
4290e3d5408SPeter Wemm     if (oldhash(SP_PARM))
4300e3d5408SPeter Wemm 	oldhash(SP_PARM)[i] = hash(SP_PARM, OLDTEXT(SP_PARM, i));
4310e3d5408SPeter Wemm }
4320e3d5408SPeter Wemm 
4330e3d5408SPeter Wemm #if NCURSES_SP_FUNCS
4340e3d5408SPeter Wemm NCURSES_EXPORT(void)
_nc_make_oldhash(int i)4350e3d5408SPeter Wemm _nc_make_oldhash(int i)
4360e3d5408SPeter Wemm {
4370e3d5408SPeter Wemm     NCURSES_SP_NAME(_nc_make_oldhash) (CURRENT_SCREEN, i);
4380e3d5408SPeter Wemm }
4390e3d5408SPeter Wemm #endif
4400e3d5408SPeter Wemm 
4410e3d5408SPeter Wemm NCURSES_EXPORT(void)
NCURSES_SP_NAME(_nc_scroll_oldhash)4420e3d5408SPeter Wemm NCURSES_SP_NAME(_nc_scroll_oldhash) (NCURSES_SP_DCLx int n, int top, int bot)
4430e3d5408SPeter Wemm {
4440e3d5408SPeter Wemm     size_t size;
4450e3d5408SPeter Wemm     int i;
4460e3d5408SPeter Wemm 
4470e3d5408SPeter Wemm     if (!oldhash(SP_PARM))
4480e3d5408SPeter Wemm 	return;
4490e3d5408SPeter Wemm 
4505ca44d1cSRong-En Fan     size = sizeof(*(oldhash(SP_PARM))) * (size_t) (bot - top + 1 - abs(n));
4515ca44d1cSRong-En Fan     if (n > 0) {
4525ca44d1cSRong-En Fan 	memmove(oldhash(SP_PARM) + top, oldhash(SP_PARM) + top + n, size);
4535ca44d1cSRong-En Fan 	for (i = bot; i > bot - n; i--)
4547a69bbfbSPeter Wemm 	    oldhash(SP_PARM)[i] = hash(SP_PARM, OLDTEXT(SP_PARM, i));
4550e3d5408SPeter Wemm     } else {
4560e3d5408SPeter Wemm 	memmove(oldhash(SP_PARM) + top - n, oldhash(SP_PARM) + top, size);
4575ca44d1cSRong-En Fan 	for (i = top; i < top - n; i++)
4580e3d5408SPeter Wemm 	    oldhash(SP_PARM)[i] = hash(SP_PARM, OLDTEXT(SP_PARM, i));
4590e3d5408SPeter Wemm     }
4600e3d5408SPeter Wemm }
4610e3d5408SPeter Wemm 
4620e3d5408SPeter Wemm #if NCURSES_SP_FUNCS
4630e3d5408SPeter Wemm NCURSES_EXPORT(void)
_nc_scroll_oldhash(int n,int top,int bot)4640e3d5408SPeter Wemm _nc_scroll_oldhash(int n, int top, int bot)
4650e3d5408SPeter Wemm {
4667a69bbfbSPeter Wemm     NCURSES_SP_NAME(_nc_scroll_oldhash) (CURRENT_SCREEN, n, top, bot);
4670e3d5408SPeter Wemm }
4680e3d5408SPeter Wemm #endif
4695ca44d1cSRong-En Fan 
4700e3d5408SPeter Wemm #ifdef HASHDEBUG
4717a69bbfbSPeter Wemm static void
usage(void)4720e3d5408SPeter Wemm usage(void)
4730e3d5408SPeter Wemm {
4740e3d5408SPeter Wemm     static const char *table[] =
4750e3d5408SPeter Wemm     {
4760e3d5408SPeter Wemm 	"hashmap test-driver",
4777a69bbfbSPeter Wemm 	"",
4780e3d5408SPeter Wemm 	"#  comment",
4790e3d5408SPeter Wemm 	"l  get initial line number vector",
4800e3d5408SPeter Wemm 	"n  use following letters as text of new lines",
4810e3d5408SPeter Wemm 	"o  use following letters as text of old lines",
4820e3d5408SPeter Wemm 	"d  dump state of test arrays",
4830e3d5408SPeter Wemm 	"h  apply hash mapper and see scroll optimization",
4840e3d5408SPeter Wemm 	"?  this message"
4850e3d5408SPeter Wemm     };
4860e3d5408SPeter Wemm     size_t n;
4870e3d5408SPeter Wemm     for (n = 0; n < sizeof(table) / sizeof(table[0]); n++)
4880e3d5408SPeter Wemm 	fprintf(stderr, "%s\n", table[n]);
4890e3d5408SPeter Wemm }
4900e3d5408SPeter Wemm 
4915ca44d1cSRong-En Fan int
main(int argc GCC_UNUSED,char * argv[]GCC_UNUSED)4920e3d5408SPeter Wemm main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
4930e3d5408SPeter Wemm {
4940e3d5408SPeter Wemm     char line[BUFSIZ], *st;
4950e3d5408SPeter Wemm     int n;
4965ca44d1cSRong-En Fan 
4970e3d5408SPeter Wemm     if (setupterm(NULL, fileno(stdout), (int *) 0) == ERR)
4980e3d5408SPeter Wemm 	return EXIT_FAILURE;
4990e3d5408SPeter Wemm     (void) _nc_alloc_screen();
5000e3d5408SPeter Wemm 
5015ca44d1cSRong-En Fan     for (n = 0; n < screen_lines(sp); n++) {
5020e3d5408SPeter Wemm 	reallines[n] = n;
5030e3d5408SPeter Wemm 	oldnums[n] = _NEWINDEX;
5040e3d5408SPeter Wemm 	CharOf(oldtext[n][0]) = CharOf(newtext[n][0]) = '.';
5050e3d5408SPeter Wemm     }
5065ca44d1cSRong-En Fan 
5070e3d5408SPeter Wemm     if (NC_ISATTY(fileno(stdin)))
5080e3d5408SPeter Wemm 	usage();
5090e3d5408SPeter Wemm 
5100e3d5408SPeter Wemm #ifdef TRACE
5110e3d5408SPeter Wemm     _nc_tracing = TRACE_MOVE;
5120e3d5408SPeter Wemm #endif
5130e3d5408SPeter Wemm     for (;;) {
5140e3d5408SPeter Wemm 	/* grab a test command */
5155ca44d1cSRong-En Fan 	if (fgets(line, sizeof(line), stdin) == (char *) NULL)
5160e3d5408SPeter Wemm 	    break;
5170e3d5408SPeter Wemm 
5180e3d5408SPeter Wemm 	switch (line[0]) {
5190e3d5408SPeter Wemm 	case '#':		/* comment */
5205ca44d1cSRong-En Fan 	    (void) fputs(line, stderr);
5210e3d5408SPeter Wemm 	    break;
5220e3d5408SPeter Wemm 
5230e3d5408SPeter Wemm 	case 'l':		/* get initial line number vector */
5240e3d5408SPeter Wemm 	    for (n = 0; n < screen_lines(sp); n++) {
5250e3d5408SPeter Wemm 		reallines[n] = n;
5260e3d5408SPeter Wemm 		oldnums[n] = _NEWINDEX;
5270e3d5408SPeter Wemm 	    }
5280e3d5408SPeter Wemm 	    n = 0;
5290e3d5408SPeter Wemm 	    st = strtok(line, " ");
5300e3d5408SPeter Wemm 	    do {
5310e3d5408SPeter Wemm 		oldnums[n++] = atoi(st);
5320e3d5408SPeter Wemm 	    } while
5330e3d5408SPeter Wemm 		((st = strtok((char *) NULL, " ")) != 0);
5345ca44d1cSRong-En Fan 	    break;
5350e3d5408SPeter Wemm 
5360e3d5408SPeter Wemm 	case 'n':		/* use following letters as text of new lines */
5370e3d5408SPeter Wemm 	    for (n = 0; n < screen_lines(sp); n++)
5380e3d5408SPeter Wemm 		CharOf(newtext[n][0]) = '.';
5390e3d5408SPeter Wemm 	    for (n = 0; n < screen_lines(sp); n++)
5405ca44d1cSRong-En Fan 		if (line[n + 1] == '\n')
5415ca44d1cSRong-En Fan 		    break;
5425ca44d1cSRong-En Fan 		else
5430e3d5408SPeter Wemm 		    CharOf(newtext[n][0]) = line[n + 1];
5445ca44d1cSRong-En Fan 	    break;
5450e3d5408SPeter Wemm 
5460e3d5408SPeter Wemm 	case 'o':		/* use following letters as text of old lines */
5470e3d5408SPeter Wemm 	    for (n = 0; n < screen_lines(sp); n++)
5480e3d5408SPeter Wemm 		CharOf(oldtext[n][0]) = '.';
5490e3d5408SPeter Wemm 	    for (n = 0; n < screen_lines(sp); n++)
550 		if (line[n + 1] == '\n')
551 		    break;
552 		else
553 		    CharOf(oldtext[n][0]) = line[n + 1];
554 	    break;
555 
556 	case 'd':		/* dump state of test arrays */
557 #ifdef TRACE
558 	    _nc_linedump();
559 #endif
560 	    (void) fputs("Old lines: [", stdout);
561 	    for (n = 0; n < screen_lines(sp); n++)
562 		putchar(CharOf(oldtext[n][0]));
563 	    putchar(']');
564 	    putchar('\n');
565 	    (void) fputs("New lines: [", stdout);
566 	    for (n = 0; n < screen_lines(sp); n++)
567 		putchar(CharOf(newtext[n][0]));
568 	    putchar(']');
569 	    putchar('\n');
570 	    break;
571 
572 	case 'h':		/* apply hash mapper and see scroll optimization */
573 	    _nc_hash_map();
574 	    (void) fputs("Result:\n", stderr);
575 #ifdef TRACE
576 	    _nc_linedump();
577 #endif
578 	    _nc_scroll_optimize();
579 	    (void) fputs("Done.\n", stderr);
580 	    break;
581 	default:
582 	case '?':
583 	    usage();
584 	    break;
585 	}
586     }
587     exit_curses(EXIT_SUCCESS);
588 }
589 
590 #endif /* HASHDEBUG */
591 
592 /* hashmap.c ends here */
593