Home | History | Annotate | Line # | Download | only in gnulib-lib
      1  1.1  christos /* Functions to make fuzzy comparisons between strings
      2  1.1  christos    Copyright (C) 1988-1989, 1992-1993, 1995, 2001-2003, 2006 Free Software Foundation, Inc.
      3  1.1  christos 
      4  1.1  christos    This program is free software; you can redistribute it and/or modify
      5  1.1  christos    it under the terms of the GNU General Public License as published by
      6  1.1  christos    the Free Software Foundation; either version 2 of the License, or (at
      7  1.1  christos    your option) any later version.
      8  1.1  christos 
      9  1.1  christos    This program is distributed in the hope that it will be useful, but
     10  1.1  christos    WITHOUT ANY WARRANTY; without even the implied warranty of
     11  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  1.1  christos    General Public License for more details.
     13  1.1  christos 
     14  1.1  christos    You should have received a copy of the GNU General Public License
     15  1.1  christos    along with this program; if not, write to the Free Software
     16  1.1  christos    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     17  1.1  christos 
     18  1.1  christos 
     19  1.1  christos    Derived from GNU diff 2.7, analyze.c et al.
     20  1.1  christos 
     21  1.1  christos    The basic idea is to consider two strings as similar if, when
     22  1.1  christos    transforming the first string into the second string through a
     23  1.1  christos    sequence of edits (inserts and deletes of one character each),
     24  1.1  christos    this sequence is short - or equivalently, if the ordered list
     25  1.1  christos    of characters that are untouched by these edits is long.  For a
     26  1.1  christos    good introduction to the subject, read about the "Levenshtein
     27  1.1  christos    distance" in Wikipedia.
     28  1.1  christos 
     29  1.1  christos    The basic algorithm is described in:
     30  1.1  christos    "An O(ND) Difference Algorithm and its Variations", Eugene Myers,
     31  1.1  christos    Algorithmica Vol. 1 No. 2, 1986, pp. 251-266;
     32  1.1  christos    see especially section 4.2, which describes the variation used below.
     33  1.1  christos 
     34  1.1  christos    The basic algorithm was independently discovered as described in:
     35  1.1  christos    "Algorithms for Approximate String Matching", E. Ukkonen,
     36  1.1  christos    Information and Control Vol. 64, 1985, pp. 100-118.
     37  1.1  christos 
     38  1.1  christos    Unless the 'minimal' flag is set, this code uses the TOO_EXPENSIVE
     39  1.1  christos    heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N)
     40  1.1  christos    at the price of producing suboptimal output for large inputs with
     41  1.1  christos    many differences.
     42  1.1  christos 
     43  1.1  christos    Modified to work on strings rather than files
     44  1.1  christos    by Peter Miller <pmiller (at) agso.gov.au>, October 1995 */
     45  1.1  christos 
     46  1.1  christos #include <config.h>
     47  1.1  christos 
     48  1.1  christos /* Specification.  */
     49  1.1  christos #include "fstrcmp.h"
     50  1.1  christos 
     51  1.1  christos #include <string.h>
     52  1.1  christos #include <stdio.h>
     53  1.1  christos #include <stdlib.h>
     54  1.1  christos #include <limits.h>
     55  1.1  christos 
     56  1.1  christos #include "lock.h"
     57  1.1  christos #include "tls.h"
     58  1.1  christos #include "xalloc.h"
     59  1.1  christos 
     60  1.1  christos #ifndef uintptr_t
     61  1.1  christos # define uintptr_t unsigned long
     62  1.1  christos #endif
     63  1.1  christos 
     64  1.1  christos 
     65  1.1  christos /*
     66  1.1  christos  * Context of comparison operation.
     67  1.1  christos  */
     68  1.1  christos struct context
     69  1.1  christos {
     70  1.1  christos   /*
     71  1.1  christos    * Data on one input string being compared.
     72  1.1  christos    */
     73  1.1  christos   struct string_data
     74  1.1  christos   {
     75  1.1  christos     /* The string to be compared. */
     76  1.1  christos     const char *data;
     77  1.1  christos 
     78  1.1  christos     /* The length of the string to be compared. */
     79  1.1  christos     int data_length;
     80  1.1  christos 
     81  1.1  christos     /* The number of characters inserted or deleted. */
     82  1.1  christos     int edit_count;
     83  1.1  christos   }
     84  1.1  christos   string[2];
     85  1.1  christos 
     86  1.1  christos   #ifdef MINUS_H_FLAG
     87  1.1  christos 
     88  1.1  christos   /* This corresponds to the diff -H flag.  With this heuristic, for
     89  1.1  christos      strings with a constant small density of changes, the algorithm is
     90  1.1  christos      linear in the strings size.  This is unlikely in typical uses of
     91  1.1  christos      fstrcmp, and so is usually compiled out.  Besides, there is no
     92  1.1  christos      interface to set it true.  */
     93  1.1  christos   int heuristic;
     94  1.1  christos 
     95  1.1  christos   #endif
     96  1.1  christos 
     97  1.1  christos   /* Vector, indexed by diagonal, containing 1 + the X coordinate of the
     98  1.1  christos      point furthest along the given diagonal in the forward search of the
     99  1.1  christos      edit matrix.  */
    100  1.1  christos   int *fdiag;
    101  1.1  christos 
    102  1.1  christos   /* Vector, indexed by diagonal, containing the X coordinate of the point
    103  1.1  christos      furthest along the given diagonal in the backward search of the edit
    104  1.1  christos      matrix.  */
    105  1.1  christos   int *bdiag;
    106  1.1  christos 
    107  1.1  christos   /* Edit scripts longer than this are too expensive to compute.  */
    108  1.1  christos   int too_expensive;
    109  1.1  christos 
    110  1.1  christos   /* Snakes bigger than this are considered `big'.  */
    111  1.1  christos   #define SNAKE_LIMIT	20
    112  1.1  christos };
    113  1.1  christos 
    114  1.1  christos struct partition
    115  1.1  christos {
    116  1.1  christos   /* Midpoints of this partition.  */
    117  1.1  christos   int xmid, ymid;
    118  1.1  christos 
    119  1.1  christos   /* Nonzero if low half will be analyzed minimally.  */
    120  1.1  christos   int lo_minimal;
    121  1.1  christos 
    122  1.1  christos   /* Likewise for high half.  */
    123  1.1  christos   int hi_minimal;
    124  1.1  christos };
    125  1.1  christos 
    126  1.1  christos 
    127  1.1  christos /* NAME
    128  1.1  christos 	diag - find diagonal path
    129  1.1  christos 
    130  1.1  christos    SYNOPSIS
    131  1.1  christos 	int diag(int xoff, int xlim, int yoff, int ylim, int minimal,
    132  1.1  christos 		 struct partition *part, struct context *ctxt);
    133  1.1  christos 
    134  1.1  christos    DESCRIPTION
    135  1.1  christos 	Find the midpoint of the shortest edit script for a specified
    136  1.1  christos 	portion of the two strings.
    137  1.1  christos 
    138  1.1  christos 	Scan from the beginnings of the strings, and simultaneously from
    139  1.1  christos 	the ends, doing a breadth-first search through the space of
    140  1.1  christos 	edit-sequence.  When the two searches meet, we have found the
    141  1.1  christos 	midpoint of the shortest edit sequence.
    142  1.1  christos 
    143  1.1  christos 	If MINIMAL is nonzero, find the minimal edit script regardless
    144  1.1  christos 	of expense.  Otherwise, if the search is too expensive, use
    145  1.1  christos 	heuristics to stop the search and report a suboptimal answer.
    146  1.1  christos 
    147  1.1  christos    RETURNS
    148  1.1  christos 	Set PART->(XMID,YMID) to the midpoint (XMID,YMID).  The diagonal
    149  1.1  christos 	number XMID - YMID equals the number of inserted characters
    150  1.1  christos 	minus the number of deleted characters (counting only characters
    151  1.1  christos 	before the midpoint).  Return the approximate edit cost; this is
    152  1.1  christos 	the total number of characters inserted or deleted (counting
    153  1.1  christos 	only characters before the midpoint), unless a heuristic is used
    154  1.1  christos 	to terminate the search prematurely.
    155  1.1  christos 
    156  1.1  christos 	Set PART->LEFT_MINIMAL to nonzero iff the minimal edit script
    157  1.1  christos 	for the left half of the partition is known; similarly for
    158  1.1  christos 	PART->RIGHT_MINIMAL.
    159  1.1  christos 
    160  1.1  christos    CAVEAT
    161  1.1  christos 	This function assumes that the first characters of the specified
    162  1.1  christos 	portions of the two strings do not match, and likewise that the
    163  1.1  christos 	last characters do not match.  The caller must trim matching
    164  1.1  christos 	characters from the beginning and end of the portions it is
    165  1.1  christos 	going to specify.
    166  1.1  christos 
    167  1.1  christos 	If we return the "wrong" partitions, the worst this can do is
    168  1.1  christos 	cause suboptimal diff output.  It cannot cause incorrect diff
    169  1.1  christos 	output.  */
    170  1.1  christos 
    171  1.1  christos static int
    172  1.1  christos diag (int xoff, int xlim, int yoff, int ylim, int minimal,
    173  1.1  christos       struct partition *part, struct context *ctxt)
    174  1.1  christos {
    175  1.1  christos   int *const fd = ctxt->fdiag;	/* Give the compiler a chance. */
    176  1.1  christos   int *const bd = ctxt->bdiag;	/* Additional help for the compiler. */
    177  1.1  christos   const char *const xv = ctxt->string[0].data;	/* Still more help for the compiler. */
    178  1.1  christos   const char *const yv = ctxt->string[1].data;	/* And more and more . . . */
    179  1.1  christos   const int dmin = xoff - ylim;	/* Minimum valid diagonal. */
    180  1.1  christos   const int dmax = xlim - yoff;	/* Maximum valid diagonal. */
    181  1.1  christos   const int fmid = xoff - yoff;	/* Center diagonal of top-down search. */
    182  1.1  christos   const int bmid = xlim - ylim;	/* Center diagonal of bottom-up search. */
    183  1.1  christos   int fmin = fmid;
    184  1.1  christos   int fmax = fmid;		/* Limits of top-down search. */
    185  1.1  christos   int bmin = bmid;
    186  1.1  christos   int bmax = bmid;		/* Limits of bottom-up search. */
    187  1.1  christos   int c;			/* Cost. */
    188  1.1  christos   int odd = (fmid - bmid) & 1;
    189  1.1  christos 
    190  1.1  christos   /*
    191  1.1  christos    * True if southeast corner is on an odd diagonal with respect
    192  1.1  christos    * to the northwest.
    193  1.1  christos    */
    194  1.1  christos   fd[fmid] = xoff;
    195  1.1  christos   bd[bmid] = xlim;
    196  1.1  christos   for (c = 1;; ++c)
    197  1.1  christos     {
    198  1.1  christos       int d;			/* Active diagonal. */
    199  1.1  christos       int big_snake;
    200  1.1  christos 
    201  1.1  christos       big_snake = 0;
    202  1.1  christos       /* Extend the top-down search by an edit step in each diagonal. */
    203  1.1  christos       if (fmin > dmin)
    204  1.1  christos 	fd[--fmin - 1] = -1;
    205  1.1  christos       else
    206  1.1  christos 	++fmin;
    207  1.1  christos       if (fmax < dmax)
    208  1.1  christos 	fd[++fmax + 1] = -1;
    209  1.1  christos       else
    210  1.1  christos 	--fmax;
    211  1.1  christos       for (d = fmax; d >= fmin; d -= 2)
    212  1.1  christos 	{
    213  1.1  christos 	  int x;
    214  1.1  christos 	  int y;
    215  1.1  christos 	  int oldx;
    216  1.1  christos 	  int tlo;
    217  1.1  christos 	  int thi;
    218  1.1  christos 
    219  1.1  christos 	  tlo = fd[d - 1],
    220  1.1  christos 	    thi = fd[d + 1];
    221  1.1  christos 
    222  1.1  christos 	  if (tlo >= thi)
    223  1.1  christos 	    x = tlo + 1;
    224  1.1  christos 	  else
    225  1.1  christos 	    x = thi;
    226  1.1  christos 	  oldx = x;
    227  1.1  christos 	  y = x - d;
    228  1.1  christos 	  while (x < xlim && y < ylim && xv[x] == yv[y])
    229  1.1  christos 	    {
    230  1.1  christos 	      ++x;
    231  1.1  christos 	      ++y;
    232  1.1  christos 	    }
    233  1.1  christos 	  if (x - oldx > SNAKE_LIMIT)
    234  1.1  christos 	    big_snake = 1;
    235  1.1  christos 	  fd[d] = x;
    236  1.1  christos 	  if (odd && bmin <= d && d <= bmax && bd[d] <= x)
    237  1.1  christos 	    {
    238  1.1  christos 	      part->xmid = x;
    239  1.1  christos 	      part->ymid = y;
    240  1.1  christos 	      part->lo_minimal = part->hi_minimal = 1;
    241  1.1  christos 	      return 2 * c - 1;
    242  1.1  christos 	    }
    243  1.1  christos 	}
    244  1.1  christos       /* Similarly extend the bottom-up search.  */
    245  1.1  christos       if (bmin > dmin)
    246  1.1  christos 	bd[--bmin - 1] = INT_MAX;
    247  1.1  christos       else
    248  1.1  christos 	++bmin;
    249  1.1  christos       if (bmax < dmax)
    250  1.1  christos 	bd[++bmax + 1] = INT_MAX;
    251  1.1  christos       else
    252  1.1  christos 	--bmax;
    253  1.1  christos       for (d = bmax; d >= bmin; d -= 2)
    254  1.1  christos 	{
    255  1.1  christos 	  int x;
    256  1.1  christos 	  int y;
    257  1.1  christos 	  int oldx;
    258  1.1  christos 	  int tlo;
    259  1.1  christos 	  int thi;
    260  1.1  christos 
    261  1.1  christos 	  tlo = bd[d - 1],
    262  1.1  christos 	    thi = bd[d + 1];
    263  1.1  christos 	  if (tlo < thi)
    264  1.1  christos 	    x = tlo;
    265  1.1  christos 	  else
    266  1.1  christos 	    x = thi - 1;
    267  1.1  christos 	  oldx = x;
    268  1.1  christos 	  y = x - d;
    269  1.1  christos 	  while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1])
    270  1.1  christos 	    {
    271  1.1  christos 	      --x;
    272  1.1  christos 	      --y;
    273  1.1  christos 	    }
    274  1.1  christos 	  if (oldx - x > SNAKE_LIMIT)
    275  1.1  christos 	    big_snake = 1;
    276  1.1  christos 	  bd[d] = x;
    277  1.1  christos 	  if (!odd && fmin <= d && d <= fmax && x <= fd[d])
    278  1.1  christos 	    {
    279  1.1  christos 	      part->xmid = x;
    280  1.1  christos 	      part->ymid = y;
    281  1.1  christos 	      part->lo_minimal = part->hi_minimal = 1;
    282  1.1  christos 	      return 2 * c;
    283  1.1  christos 	    }
    284  1.1  christos 	}
    285  1.1  christos 
    286  1.1  christos       if (minimal)
    287  1.1  christos 	continue;
    288  1.1  christos 
    289  1.1  christos #ifdef MINUS_H_FLAG
    290  1.1  christos       /* Heuristic: check occasionally for a diagonal that has made lots
    291  1.1  christos          of progress compared with the edit distance.  If we have any
    292  1.1  christos          such, find the one that has made the most progress and return
    293  1.1  christos          it as if it had succeeded.
    294  1.1  christos 
    295  1.1  christos          With this heuristic, for strings with a constant small density
    296  1.1  christos          of changes, the algorithm is linear in the strings size.  */
    297  1.1  christos       if (c > 200 && big_snake && ctxt->heuristic)
    298  1.1  christos 	{
    299  1.1  christos 	  int best;
    300  1.1  christos 
    301  1.1  christos 	  best = 0;
    302  1.1  christos 	  for (d = fmax; d >= fmin; d -= 2)
    303  1.1  christos 	    {
    304  1.1  christos 	      int dd;
    305  1.1  christos 	      int x;
    306  1.1  christos 	      int y;
    307  1.1  christos 	      int v;
    308  1.1  christos 
    309  1.1  christos 	      dd = d - fmid;
    310  1.1  christos 	      x = fd[d];
    311  1.1  christos 	      y = x - d;
    312  1.1  christos 	      v = (x - xoff) * 2 - dd;
    313  1.1  christos 
    314  1.1  christos 	      if (v > 12 * (c + (dd < 0 ? -dd : dd)))
    315  1.1  christos 		{
    316  1.1  christos 		  if
    317  1.1  christos 		    (
    318  1.1  christos 		      v > best
    319  1.1  christos 		      &&
    320  1.1  christos 		      xoff + SNAKE_LIMIT <= x
    321  1.1  christos 		      &&
    322  1.1  christos 		      x < xlim
    323  1.1  christos 		      &&
    324  1.1  christos 		      yoff + SNAKE_LIMIT <= y
    325  1.1  christos 		      &&
    326  1.1  christos 		      y < ylim
    327  1.1  christos 		    )
    328  1.1  christos 		    {
    329  1.1  christos 		      /* We have a good enough best diagonal; now insist
    330  1.1  christos 			 that it end with a significant snake.  */
    331  1.1  christos 		      int k;
    332  1.1  christos 
    333  1.1  christos 		      for (k = 1; xv[x - k] == yv[y - k]; k++)
    334  1.1  christos 			{
    335  1.1  christos 			  if (k == SNAKE_LIMIT)
    336  1.1  christos 			    {
    337  1.1  christos 			      best = v;
    338  1.1  christos 			      part->xmid = x;
    339  1.1  christos 			      part->ymid = y;
    340  1.1  christos 			      break;
    341  1.1  christos 			    }
    342  1.1  christos 			}
    343  1.1  christos 		    }
    344  1.1  christos 		}
    345  1.1  christos 	    }
    346  1.1  christos 	  if (best > 0)
    347  1.1  christos 	    {
    348  1.1  christos 	      part->lo_minimal = 1;
    349  1.1  christos 	      part->hi_minimal = 0;
    350  1.1  christos 	      return 2 * c - 1;
    351  1.1  christos 	    }
    352  1.1  christos 	  best = 0;
    353  1.1  christos 	  for (d = bmax; d >= bmin; d -= 2)
    354  1.1  christos 	    {
    355  1.1  christos 	      int dd;
    356  1.1  christos 	      int x;
    357  1.1  christos 	      int y;
    358  1.1  christos 	      int v;
    359  1.1  christos 
    360  1.1  christos 	      dd = d - bmid;
    361  1.1  christos 	      x = bd[d];
    362  1.1  christos 	      y = x - d;
    363  1.1  christos 	      v = (xlim - x) * 2 + dd;
    364  1.1  christos 
    365  1.1  christos 	      if (v > 12 * (c + (dd < 0 ? -dd : dd)))
    366  1.1  christos 		{
    367  1.1  christos 		  if (v > best && xoff < x && x <= xlim - SNAKE_LIMIT &&
    368  1.1  christos 		      yoff < y && y <= ylim - SNAKE_LIMIT)
    369  1.1  christos 		    {
    370  1.1  christos 		      /* We have a good enough best diagonal; now insist
    371  1.1  christos 			 that it end with a significant snake.  */
    372  1.1  christos 		      int k;
    373  1.1  christos 
    374  1.1  christos 		      for (k = 0; xv[x + k] == yv[y + k]; k++)
    375  1.1  christos 			{
    376  1.1  christos 			  if (k == SNAKE_LIMIT - 1)
    377  1.1  christos 			    {
    378  1.1  christos 			      best = v;
    379  1.1  christos 			      part->xmid = x;
    380  1.1  christos 			      part->ymid = y;
    381  1.1  christos 			      break;
    382  1.1  christos 			    }
    383  1.1  christos 			}
    384  1.1  christos 		    }
    385  1.1  christos 		}
    386  1.1  christos 	    }
    387  1.1  christos 	  if (best > 0)
    388  1.1  christos 	    {
    389  1.1  christos 	      part->lo_minimal = 0;
    390  1.1  christos 	      part->hi_minimal = 1;
    391  1.1  christos 	      return 2 * c - 1;
    392  1.1  christos 	    }
    393  1.1  christos 	}
    394  1.1  christos #endif /* MINUS_H_FLAG */
    395  1.1  christos 
    396  1.1  christos       /* Heuristic: if we've gone well beyond the call of duty, give up
    397  1.1  christos 	 and report halfway between our best results so far.  */
    398  1.1  christos       if (c >= ctxt->too_expensive)
    399  1.1  christos 	{
    400  1.1  christos 	  int fxybest;
    401  1.1  christos 	  int fxbest;
    402  1.1  christos 	  int bxybest;
    403  1.1  christos 	  int bxbest;
    404  1.1  christos 
    405  1.1  christos 	  /* Pacify `gcc -Wall'. */
    406  1.1  christos 	  fxbest = 0;
    407  1.1  christos 	  bxbest = 0;
    408  1.1  christos 
    409  1.1  christos 	  /* Find forward diagonal that maximizes X + Y.  */
    410  1.1  christos 	  fxybest = -1;
    411  1.1  christos 	  for (d = fmax; d >= fmin; d -= 2)
    412  1.1  christos 	    {
    413  1.1  christos 	      int x;
    414  1.1  christos 	      int y;
    415  1.1  christos 
    416  1.1  christos 	      x = fd[d] < xlim ? fd[d] : xlim;
    417  1.1  christos 	      y = x - d;
    418  1.1  christos 
    419  1.1  christos 	      if (ylim < y)
    420  1.1  christos 		{
    421  1.1  christos 		  x = ylim + d;
    422  1.1  christos 		  y = ylim;
    423  1.1  christos 		}
    424  1.1  christos 	      if (fxybest < x + y)
    425  1.1  christos 		{
    426  1.1  christos 		  fxybest = x + y;
    427  1.1  christos 		  fxbest = x;
    428  1.1  christos 		}
    429  1.1  christos 	    }
    430  1.1  christos 	  /* Find backward diagonal that minimizes X + Y.  */
    431  1.1  christos 	  bxybest = INT_MAX;
    432  1.1  christos 	  for (d = bmax; d >= bmin; d -= 2)
    433  1.1  christos 	    {
    434  1.1  christos 	      int x;
    435  1.1  christos 	      int y;
    436  1.1  christos 
    437  1.1  christos 	      x = xoff > bd[d] ? xoff : bd[d];
    438  1.1  christos 	      y = x - d;
    439  1.1  christos 
    440  1.1  christos 	      if (y < yoff)
    441  1.1  christos 		{
    442  1.1  christos 		  x = yoff + d;
    443  1.1  christos 		  y = yoff;
    444  1.1  christos 		}
    445  1.1  christos 	      if (x + y < bxybest)
    446  1.1  christos 		{
    447  1.1  christos 		  bxybest = x + y;
    448  1.1  christos 		  bxbest = x;
    449  1.1  christos 		}
    450  1.1  christos 	    }
    451  1.1  christos 	  /* Use the better of the two diagonals.  */
    452  1.1  christos 	  if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff))
    453  1.1  christos 	    {
    454  1.1  christos 	      part->xmid = fxbest;
    455  1.1  christos 	      part->ymid = fxybest - fxbest;
    456  1.1  christos 	      part->lo_minimal = 1;
    457  1.1  christos 	      part->hi_minimal = 0;
    458  1.1  christos 	    }
    459  1.1  christos 	  else
    460  1.1  christos 	    {
    461  1.1  christos 	      part->xmid = bxbest;
    462  1.1  christos 	      part->ymid = bxybest - bxbest;
    463  1.1  christos 	      part->lo_minimal = 0;
    464  1.1  christos 	      part->hi_minimal = 1;
    465  1.1  christos 	    }
    466  1.1  christos 	  return 2 * c - 1;
    467  1.1  christos 	}
    468  1.1  christos     }
    469  1.1  christos }
    470  1.1  christos 
    471  1.1  christos 
    472  1.1  christos /* NAME
    473  1.1  christos 	compareseq - find edit sequence
    474  1.1  christos 
    475  1.1  christos    SYNOPSIS
    476  1.1  christos 	void compareseq(int xoff, int xlim, int yoff, int ylim, int minimal,
    477  1.1  christos 			struct context *ctxt);
    478  1.1  christos 
    479  1.1  christos    DESCRIPTION
    480  1.1  christos 	Compare in detail contiguous subsequences of the two strings
    481  1.1  christos 	which are known, as a whole, to match each other.
    482  1.1  christos 
    483  1.1  christos 	The subsequence of string 0 is [XOFF, XLIM) and likewise for
    484  1.1  christos 	string 1.
    485  1.1  christos 
    486  1.1  christos 	Note that XLIM, YLIM are exclusive bounds.  All character
    487  1.1  christos 	numbers are origin-0.
    488  1.1  christos 
    489  1.1  christos 	If MINIMAL is nonzero, find a minimal difference no matter how
    490  1.1  christos 	expensive it is.  */
    491  1.1  christos 
    492  1.1  christos static void
    493  1.1  christos compareseq (int xoff, int xlim, int yoff, int ylim, int minimal,
    494  1.1  christos 	    struct context *ctxt)
    495  1.1  christos {
    496  1.1  christos   const char *const xv = ctxt->string[0].data;	/* Help the compiler.  */
    497  1.1  christos   const char *const yv = ctxt->string[1].data;
    498  1.1  christos 
    499  1.1  christos   /* Slide down the bottom initial diagonal. */
    500  1.1  christos   while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff])
    501  1.1  christos     {
    502  1.1  christos       ++xoff;
    503  1.1  christos       ++yoff;
    504  1.1  christos     }
    505  1.1  christos 
    506  1.1  christos   /* Slide up the top initial diagonal. */
    507  1.1  christos   while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1])
    508  1.1  christos     {
    509  1.1  christos       --xlim;
    510  1.1  christos       --ylim;
    511  1.1  christos     }
    512  1.1  christos 
    513  1.1  christos   /* Handle simple cases. */
    514  1.1  christos   if (xoff == xlim)
    515  1.1  christos     {
    516  1.1  christos       while (yoff < ylim)
    517  1.1  christos 	{
    518  1.1  christos 	  ctxt->string[1].edit_count++;
    519  1.1  christos 	  ++yoff;
    520  1.1  christos 	}
    521  1.1  christos     }
    522  1.1  christos   else if (yoff == ylim)
    523  1.1  christos     {
    524  1.1  christos       while (xoff < xlim)
    525  1.1  christos 	{
    526  1.1  christos 	  ctxt->string[0].edit_count++;
    527  1.1  christos 	  ++xoff;
    528  1.1  christos 	}
    529  1.1  christos     }
    530  1.1  christos   else
    531  1.1  christos     {
    532  1.1  christos       int c;
    533  1.1  christos       struct partition part;
    534  1.1  christos 
    535  1.1  christos       /* Find a point of correspondence in the middle of the strings.  */
    536  1.1  christos       c = diag (xoff, xlim, yoff, ylim, minimal, &part, ctxt);
    537  1.1  christos       if (c == 1)
    538  1.1  christos 	{
    539  1.1  christos #if 0
    540  1.1  christos 	  /* This should be impossible, because it implies that one of
    541  1.1  christos 	     the two subsequences is empty, and that case was handled
    542  1.1  christos 	     above without calling `diag'.  Let's verify that this is
    543  1.1  christos 	     true.  */
    544  1.1  christos 	  abort ();
    545  1.1  christos #else
    546  1.1  christos 	  /* The two subsequences differ by a single insert or delete;
    547  1.1  christos 	     record it and we are done.  */
    548  1.1  christos 	  if (part.xmid - part.ymid < xoff - yoff)
    549  1.1  christos 	    ctxt->string[1].edit_count++;
    550  1.1  christos 	  else
    551  1.1  christos 	    ctxt->string[0].edit_count++;
    552  1.1  christos #endif
    553  1.1  christos 	}
    554  1.1  christos       else
    555  1.1  christos 	{
    556  1.1  christos 	  /* Use the partitions to split this problem into subproblems.  */
    557  1.1  christos 	  compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal, ctxt);
    558  1.1  christos 	  compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal, ctxt);
    559  1.1  christos 	}
    560  1.1  christos     }
    561  1.1  christos }
    562  1.1  christos 
    563  1.1  christos 
    564  1.1  christos /* Because fstrcmp is typically called multiple times, attempt to minimize
    565  1.1  christos    the number of memory allocations performed.  Thus, let a call reuse the
    566  1.1  christos    memory already allocated by the previous call, if it is sufficient.
    567  1.1  christos    To make it multithread-safe, without need for a lock that protects the
    568  1.1  christos    already allocated memory, store the allocated memory per thread.  Free
    569  1.1  christos    it only when the thread exits.  */
    570  1.1  christos 
    571  1.1  christos static gl_tls_key_t buffer_key;	/* TLS key for a 'int *' */
    572  1.1  christos static gl_tls_key_t bufmax_key;	/* TLS key for a 'size_t' */
    573  1.1  christos 
    574  1.1  christos static void
    575  1.1  christos keys_init (void)
    576  1.1  christos {
    577  1.1  christos   gl_tls_key_init (buffer_key, free);
    578  1.1  christos   gl_tls_key_init (bufmax_key, NULL);
    579  1.1  christos   /* The per-thread initial values are NULL and 0, respectively.  */
    580  1.1  christos }
    581  1.1  christos 
    582  1.1  christos /* Ensure that keys_init is called once only.  */
    583  1.1  christos gl_once_define(static, keys_init_once)
    584  1.1  christos 
    585  1.1  christos 
    586  1.1  christos /* NAME
    587  1.1  christos 	fstrcmp - fuzzy string compare
    588  1.1  christos 
    589  1.1  christos    SYNOPSIS
    590  1.1  christos 	double fstrcmp(const char *, const char *);
    591  1.1  christos 
    592  1.1  christos    DESCRIPTION
    593  1.1  christos 	The fstrcmp function may be used to compare two string for
    594  1.1  christos 	similarity.  It is very useful in reducing "cascade" or
    595  1.1  christos 	"secondary" errors in compilers or other situations where
    596  1.1  christos 	symbol tables occur.
    597  1.1  christos 
    598  1.1  christos    RETURNS
    599  1.1  christos 	double; 0 if the strings are entirly dissimilar, 1 if the
    600  1.1  christos 	strings are identical, and a number in between if they are
    601  1.1  christos 	similar.  */
    602  1.1  christos 
    603  1.1  christos double
    604  1.1  christos fstrcmp (const char *string1, const char *string2)
    605  1.1  christos {
    606  1.1  christos   struct context ctxt;
    607  1.1  christos   int i;
    608  1.1  christos 
    609  1.1  christos   size_t fdiag_len;
    610  1.1  christos   int *buffer;
    611  1.1  christos   size_t bufmax;
    612  1.1  christos 
    613  1.1  christos   /* set the info for each string.  */
    614  1.1  christos   ctxt.string[0].data = string1;
    615  1.1  christos   ctxt.string[0].data_length = strlen (string1);
    616  1.1  christos   ctxt.string[1].data = string2;
    617  1.1  christos   ctxt.string[1].data_length = strlen (string2);
    618  1.1  christos 
    619  1.1  christos   /* short-circuit obvious comparisons */
    620  1.1  christos   if (ctxt.string[0].data_length == 0 && ctxt.string[1].data_length == 0)
    621  1.1  christos     return 1.0;
    622  1.1  christos   if (ctxt.string[0].data_length == 0 || ctxt.string[1].data_length == 0)
    623  1.1  christos     return 0.0;
    624  1.1  christos 
    625  1.1  christos   /* Set TOO_EXPENSIVE to be approximate square root of input size,
    626  1.1  christos      bounded below by 256.  */
    627  1.1  christos   ctxt.too_expensive = 1;
    628  1.1  christos   for (i = ctxt.string[0].data_length + ctxt.string[1].data_length;
    629  1.1  christos        i != 0;
    630  1.1  christos        i >>= 2)
    631  1.1  christos     ctxt.too_expensive <<= 1;
    632  1.1  christos   if (ctxt.too_expensive < 256)
    633  1.1  christos     ctxt.too_expensive = 256;
    634  1.1  christos 
    635  1.1  christos   /* Allocate memory for fdiag and bdiag from a thread-local pool.  */
    636  1.1  christos   fdiag_len = ctxt.string[0].data_length + ctxt.string[1].data_length + 3;
    637  1.1  christos   gl_once (keys_init_once, keys_init);
    638  1.1  christos   buffer = (int *) gl_tls_get (buffer_key);
    639  1.1  christos   bufmax = (size_t) (uintptr_t) gl_tls_get (bufmax_key);
    640  1.1  christos   if (fdiag_len > bufmax)
    641  1.1  christos     {
    642  1.1  christos       /* Need more memory.  */
    643  1.1  christos       bufmax = 2 * bufmax;
    644  1.1  christos       if (fdiag_len > bufmax)
    645  1.1  christos 	bufmax = fdiag_len;
    646  1.1  christos       /* Calling xrealloc would be a waste: buffer's contents does not need
    647  1.1  christos 	 to be preserved.  */
    648  1.1  christos       if (buffer != NULL)
    649  1.1  christos 	free (buffer);
    650  1.1  christos       buffer = (int *) xmalloc (bufmax * (2 * sizeof (int)));
    651  1.1  christos       gl_tls_set (buffer_key, buffer);
    652  1.1  christos       gl_tls_set (bufmax_key, (void *) (uintptr_t) bufmax);
    653  1.1  christos     }
    654  1.1  christos   ctxt.fdiag = buffer + ctxt.string[1].data_length + 1;
    655  1.1  christos   ctxt.bdiag = ctxt.fdiag + fdiag_len;
    656  1.1  christos 
    657  1.1  christos   /* Now do the main comparison algorithm */
    658  1.1  christos   ctxt.string[0].edit_count = 0;
    659  1.1  christos   ctxt.string[1].edit_count = 0;
    660  1.1  christos   compareseq (0, ctxt.string[0].data_length, 0, ctxt.string[1].data_length, 0,
    661  1.1  christos 	      &ctxt);
    662  1.1  christos 
    663  1.1  christos   /* The result is
    664  1.1  christos 	((number of chars in common) / (average length of the strings)).
    665  1.1  christos      This is admittedly biased towards finding that the strings are
    666  1.1  christos      similar, however it does produce meaningful results.  */
    667  1.1  christos   return ((double) (ctxt.string[0].data_length + ctxt.string[1].data_length
    668  1.1  christos 		    - ctxt.string[1].edit_count - ctxt.string[0].edit_count)
    669  1.1  christos 	  / (ctxt.string[0].data_length + ctxt.string[1].data_length));
    670  1.1  christos }
    671