Git fork
at reftables-rust 499 lines 12 kB view raw
1/* 2 * LibXDiff by Davide Libenzi ( File Differential Library ) 3 * Copyright (C) 2003 Davide Libenzi 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, see 17 * <http://www.gnu.org/licenses/>. 18 * 19 * Davide Libenzi <davidel@xmailserver.org> 20 * 21 */ 22 23#include "xinclude.h" 24 25 26long xdl_bogosqrt(long n) { 27 long i; 28 29 /* 30 * Classical integer square root approximation using shifts. 31 */ 32 for (i = 1; n > 0; n >>= 2) 33 i <<= 1; 34 35 return i; 36} 37 38 39int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, 40 xdemitcb_t *ecb) { 41 int i = 2; 42 mmbuffer_t mb[3]; 43 44 mb[0].ptr = (char *) pre; 45 mb[0].size = psize; 46 mb[1].ptr = (char *) rec; 47 mb[1].size = size; 48 if (size > 0 && rec[size - 1] != '\n') { 49 mb[2].ptr = (char *) "\n\\ No newline at end of file\n"; 50 mb[2].size = strlen(mb[2].ptr); 51 i++; 52 } 53 if (ecb->out_line(ecb->priv, mb, i) < 0) { 54 55 return -1; 56 } 57 58 return 0; 59} 60 61void *xdl_mmfile_first(mmfile_t *mmf, long *size) 62{ 63 *size = mmf->size; 64 return mmf->ptr; 65} 66 67 68long xdl_mmfile_size(mmfile_t *mmf) 69{ 70 return mmf->size; 71} 72 73 74int xdl_cha_init(chastore_t *cha, long isize, long icount) { 75 76 cha->head = cha->tail = NULL; 77 cha->isize = isize; 78 cha->nsize = icount * isize; 79 cha->ancur = cha->sncur = NULL; 80 cha->scurr = 0; 81 82 return 0; 83} 84 85 86void xdl_cha_free(chastore_t *cha) { 87 chanode_t *cur, *tmp; 88 89 for (cur = cha->head; (tmp = cur) != NULL;) { 90 cur = cur->next; 91 xdl_free(tmp); 92 } 93} 94 95 96void *xdl_cha_alloc(chastore_t *cha) { 97 chanode_t *ancur; 98 void *data; 99 100 if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) { 101 if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) { 102 103 return NULL; 104 } 105 ancur->icurr = 0; 106 ancur->next = NULL; 107 if (cha->tail) 108 cha->tail->next = ancur; 109 if (!cha->head) 110 cha->head = ancur; 111 cha->tail = ancur; 112 cha->ancur = ancur; 113 } 114 115 data = (char *) ancur + sizeof(chanode_t) + ancur->icurr; 116 ancur->icurr += cha->isize; 117 118 return data; 119} 120 121long xdl_guess_lines(mmfile_t *mf, long sample) { 122 long nl = 0, size, tsize = 0; 123 char const *data, *cur, *top; 124 125 if ((cur = data = xdl_mmfile_first(mf, &size))) { 126 for (top = data + size; nl < sample && cur < top; ) { 127 nl++; 128 if (!(cur = memchr(cur, '\n', top - cur))) 129 cur = top; 130 else 131 cur++; 132 } 133 tsize += (long) (cur - data); 134 } 135 136 if (nl && tsize) 137 nl = xdl_mmfile_size(mf) / (tsize / nl); 138 139 return nl + 1; 140} 141 142int xdl_blankline(const char *line, long size, long flags) 143{ 144 long i; 145 146 if (!(flags & XDF_WHITESPACE_FLAGS)) 147 return (size <= 1); 148 149 for (i = 0; i < size && XDL_ISSPACE(line[i]); i++) 150 ; 151 152 return (i == size); 153} 154 155/* 156 * Have we eaten everything on the line, except for an optional 157 * CR at the very end? 158 */ 159static int ends_with_optional_cr(const char *l, long s, long i) 160{ 161 int complete = s && l[s-1] == '\n'; 162 163 if (complete) 164 s--; 165 if (s == i) 166 return 1; 167 /* do not ignore CR at the end of an incomplete line */ 168 if (complete && s == i + 1 && l[i] == '\r') 169 return 1; 170 return 0; 171} 172 173int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) 174{ 175 int i1, i2; 176 177 if (s1 == s2 && !memcmp(l1, l2, s1)) 178 return 1; 179 if (!(flags & XDF_WHITESPACE_FLAGS)) 180 return 0; 181 182 i1 = 0; 183 i2 = 0; 184 185 /* 186 * -w matches everything that matches with -b, and -b in turn 187 * matches everything that matches with --ignore-space-at-eol, 188 * which in turn matches everything that matches with --ignore-cr-at-eol. 189 * 190 * Each flavor of ignoring needs different logic to skip whitespaces 191 * while we have both sides to compare. 192 */ 193 if (flags & XDF_IGNORE_WHITESPACE) { 194 goto skip_ws; 195 while (i1 < s1 && i2 < s2) { 196 if (l1[i1++] != l2[i2++]) 197 return 0; 198 skip_ws: 199 while (i1 < s1 && XDL_ISSPACE(l1[i1])) 200 i1++; 201 while (i2 < s2 && XDL_ISSPACE(l2[i2])) 202 i2++; 203 } 204 } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { 205 while (i1 < s1 && i2 < s2) { 206 if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) { 207 /* Skip matching spaces and try again */ 208 while (i1 < s1 && XDL_ISSPACE(l1[i1])) 209 i1++; 210 while (i2 < s2 && XDL_ISSPACE(l2[i2])) 211 i2++; 212 continue; 213 } 214 if (l1[i1++] != l2[i2++]) 215 return 0; 216 } 217 } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) { 218 while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { 219 i1++; 220 i2++; 221 } 222 } else if (flags & XDF_IGNORE_CR_AT_EOL) { 223 /* Find the first difference and see how the line ends */ 224 while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { 225 i1++; 226 i2++; 227 } 228 return (ends_with_optional_cr(l1, s1, i1) && 229 ends_with_optional_cr(l2, s2, i2)); 230 } 231 232 /* 233 * After running out of one side, the remaining side must have 234 * nothing but whitespace for the lines to match. Note that 235 * ignore-whitespace-at-eol case may break out of the loop 236 * while there still are characters remaining on both lines. 237 */ 238 if (i1 < s1) { 239 while (i1 < s1 && XDL_ISSPACE(l1[i1])) 240 i1++; 241 if (s1 != i1) 242 return 0; 243 } 244 if (i2 < s2) { 245 while (i2 < s2 && XDL_ISSPACE(l2[i2])) 246 i2++; 247 return (s2 == i2); 248 } 249 return 1; 250} 251 252unsigned long xdl_hash_record_with_whitespace(char const **data, 253 char const *top, long flags) { 254 unsigned long ha = 5381; 255 char const *ptr = *data; 256 int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL; 257 258 for (; ptr < top && *ptr != '\n'; ptr++) { 259 if (cr_at_eol_only) { 260 /* do not ignore CR at the end of an incomplete line */ 261 if (*ptr == '\r' && 262 (ptr + 1 < top && ptr[1] == '\n')) 263 continue; 264 } 265 else if (XDL_ISSPACE(*ptr)) { 266 const char *ptr2 = ptr; 267 int at_eol; 268 while (ptr + 1 < top && XDL_ISSPACE(ptr[1]) 269 && ptr[1] != '\n') 270 ptr++; 271 at_eol = (top <= ptr + 1 || ptr[1] == '\n'); 272 if (flags & XDF_IGNORE_WHITESPACE) 273 ; /* already handled */ 274 else if (flags & XDF_IGNORE_WHITESPACE_CHANGE 275 && !at_eol) { 276 ha += (ha << 5); 277 ha ^= (unsigned long) ' '; 278 } 279 else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL 280 && !at_eol) { 281 while (ptr2 != ptr + 1) { 282 ha += (ha << 5); 283 ha ^= (unsigned long) *ptr2; 284 ptr2++; 285 } 286 } 287 continue; 288 } 289 ha += (ha << 5); 290 ha ^= (unsigned long) *ptr; 291 } 292 *data = ptr < top ? ptr + 1: ptr; 293 294 return ha; 295} 296 297/* 298 * Compiler reassociation barrier: pretend to modify X and Y to disallow 299 * changing evaluation order with respect to following uses of X and Y. 300 */ 301#ifdef __GNUC__ 302#define REASSOC_FENCE(x, y) __asm__("" : "+r"(x), "+r"(y)) 303#else 304#define REASSOC_FENCE(x, y) 305#endif 306 307unsigned long xdl_hash_record_verbatim(char const **data, char const *top) { 308 unsigned long ha = 5381, c0, c1; 309 char const *ptr = *data; 310#if 0 311 /* 312 * The baseline form of the optimized loop below. This is the djb2 313 * hash (the above function uses a variant with XOR instead of ADD). 314 */ 315 for (; ptr < top && *ptr != '\n'; ptr++) { 316 ha += (ha << 5); 317 ha += (unsigned long) *ptr; 318 } 319 *data = ptr < top ? ptr + 1: ptr; 320#else 321 /* Process two characters per iteration. */ 322 if (top - ptr >= 2) do { 323 if ((c0 = ptr[0]) == '\n') { 324 *data = ptr + 1; 325 return ha; 326 } 327 if ((c1 = ptr[1]) == '\n') { 328 *data = ptr + 2; 329 c0 += ha; 330 REASSOC_FENCE(c0, ha); 331 ha = ha * 32 + c0; 332 return ha; 333 } 334 /* 335 * Combine characters C0 and C1 into the hash HA. We have 336 * HA = (HA * 33 + C0) * 33 + C1, and we want to ensure 337 * that dependency chain over HA is just one multiplication 338 * and one addition, i.e. we want to evaluate this as 339 * HA = HA * 33 * 33 + (C0 * 33 + C1), and likewise prefer 340 * (C0 * 32 + (C0 + C1)) for the expression in parenthesis. 341 */ 342 ha *= 33 * 33; 343 c1 += c0; 344 REASSOC_FENCE(c1, c0); 345 c1 += c0 * 32; 346 REASSOC_FENCE(c1, ha); 347 ha += c1; 348 349 ptr += 2; 350 } while (ptr < top - 1); 351 *data = top; 352 if (ptr < top && (c0 = ptr[0]) != '\n') { 353 c0 += ha; 354 REASSOC_FENCE(c0, ha); 355 ha = ha * 32 + c0; 356 } 357#endif 358 return ha; 359} 360 361unsigned int xdl_hashbits(unsigned int size) { 362 unsigned int val = 1, bits = 0; 363 364 for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++); 365 return bits ? bits: 1; 366} 367 368 369int xdl_num_out(char *out, long val) { 370 char *ptr, *str = out; 371 char buf[32]; 372 373 ptr = buf + sizeof(buf) - 1; 374 *ptr = '\0'; 375 if (val < 0) { 376 *--ptr = '-'; 377 val = -val; 378 } 379 for (; val && ptr > buf; val /= 10) 380 *--ptr = "0123456789"[val % 10]; 381 if (*ptr) 382 for (; *ptr; ptr++, str++) 383 *str = *ptr; 384 else 385 *str++ = '0'; 386 *str = '\0'; 387 388 return str - out; 389} 390 391static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2, 392 const char *func, long funclen, 393 xdemitcb_t *ecb) { 394 int nb = 0; 395 mmbuffer_t mb; 396 char buf[128]; 397 398 memcpy(buf, "@@ -", 4); 399 nb += 4; 400 401 nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1); 402 403 if (c1 != 1) { 404 memcpy(buf + nb, ",", 1); 405 nb += 1; 406 407 nb += xdl_num_out(buf + nb, c1); 408 } 409 410 memcpy(buf + nb, " +", 2); 411 nb += 2; 412 413 nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1); 414 415 if (c2 != 1) { 416 memcpy(buf + nb, ",", 1); 417 nb += 1; 418 419 nb += xdl_num_out(buf + nb, c2); 420 } 421 422 memcpy(buf + nb, " @@", 3); 423 nb += 3; 424 if (func && funclen) { 425 buf[nb++] = ' '; 426 if ((size_t)funclen > sizeof(buf) - nb - 1) 427 funclen = sizeof(buf) - nb - 1; 428 memcpy(buf + nb, func, funclen); 429 nb += funclen; 430 } 431 buf[nb++] = '\n'; 432 433 mb.ptr = buf; 434 mb.size = nb; 435 if (ecb->out_line(ecb->priv, &mb, 1) < 0) 436 return -1; 437 return 0; 438} 439 440int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, 441 const char *func, long funclen, 442 xdemitcb_t *ecb) { 443 if (!ecb->out_hunk) 444 return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb); 445 if (ecb->out_hunk(ecb->priv, 446 c1 ? s1 : s1 - 1, c1, 447 c2 ? s2 : s2 - 1, c2, 448 func, funclen) < 0) 449 return -1; 450 return 0; 451} 452 453int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, 454 int line1, int count1, int line2, int count2) 455{ 456 /* 457 * This probably does not work outside Git, since 458 * we have a very simple mmfile structure. 459 * 460 * Note: ideally, we would reuse the prepared environment, but 461 * the libxdiff interface does not (yet) allow for diffing only 462 * ranges of lines instead of the whole files. 463 */ 464 mmfile_t subfile1, subfile2; 465 xdfenv_t env; 466 467 subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr; 468 subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr + 469 diff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr; 470 subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr; 471 subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr + 472 diff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr; 473 if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0) 474 return -1; 475 476 memcpy(diff_env->xdf1.changed + line1 - 1, env.xdf1.changed, count1); 477 memcpy(diff_env->xdf2.changed + line2 - 1, env.xdf2.changed, count2); 478 479 xdl_free_env(&env); 480 481 return 0; 482} 483 484void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size) 485{ 486 void *tmp = NULL; 487 size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX; 488 if ((size_t)nr > n) 489 n = nr; 490 if (SIZE_MAX / size >= n) 491 tmp = xdl_realloc(p, n * size); 492 if (tmp) { 493 *alloc = n; 494 } else { 495 xdl_free(p); 496 *alloc = 0; 497 } 498 return tmp; 499}