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