Coverage Report

Created: 2026-01-09 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/xdiff/xutils.c
Line
Count
Source
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
26
0
long xdl_bogosqrt(long n) {
27
0
  long i;
28
29
  /*
30
   * Classical integer square root approximation using shifts.
31
   */
32
0
  for (i = 1; n > 0; n >>= 2)
33
0
    i <<= 1;
34
35
0
  return i;
36
0
}
37
38
39
int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
40
0
         xdemitcb_t *ecb) {
41
0
  int i = 2;
42
0
  mmbuffer_t mb[3];
43
44
0
  mb[0].ptr = (char *) pre;
45
0
  mb[0].size = psize;
46
0
  mb[1].ptr = (char *) rec;
47
0
  mb[1].size = size;
48
0
  if (size > 0 && rec[size - 1] != '\n') {
49
0
    mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
50
0
    mb[2].size = strlen(mb[2].ptr);
51
0
    i++;
52
0
  }
53
0
  if (ecb->out_line(ecb->priv, mb, i) < 0) {
54
55
0
    return -1;
56
0
  }
57
58
0
  return 0;
59
0
}
60
61
void *xdl_mmfile_first(mmfile_t *mmf, long *size)
62
0
{
63
0
  *size = mmf->size;
64
0
  return mmf->ptr;
65
0
}
66
67
68
long xdl_mmfile_size(mmfile_t *mmf)
69
0
{
70
0
  return mmf->size;
71
0
}
72
73
74
0
int xdl_cha_init(chastore_t *cha, long isize, long icount) {
75
76
0
  cha->head = cha->tail = NULL;
77
0
  cha->isize = isize;
78
0
  cha->nsize = icount * isize;
79
0
  cha->ancur = cha->sncur = NULL;
80
0
  cha->scurr = 0;
81
82
0
  return 0;
83
0
}
84
85
86
0
void xdl_cha_free(chastore_t *cha) {
87
0
  chanode_t *cur, *tmp;
88
89
0
  for (cur = cha->head; (tmp = cur) != NULL;) {
90
0
    cur = cur->next;
91
0
    xdl_free(tmp);
92
0
  }
93
0
}
94
95
96
0
void *xdl_cha_alloc(chastore_t *cha) {
97
0
  chanode_t *ancur;
98
0
  void *data;
99
100
0
  if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
101
0
    if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
102
103
0
      return NULL;
104
0
    }
105
0
    ancur->icurr = 0;
106
0
    ancur->next = NULL;
107
0
    if (cha->tail)
108
0
      cha->tail->next = ancur;
109
0
    if (!cha->head)
110
0
      cha->head = ancur;
111
0
    cha->tail = ancur;
112
0
    cha->ancur = ancur;
113
0
  }
114
115
0
  data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
116
0
  ancur->icurr += cha->isize;
117
118
0
  return data;
119
0
}
120
121
0
long xdl_guess_lines(mmfile_t *mf, long sample) {
122
0
  long nl = 0, size, tsize = 0;
123
0
  char const *data, *cur, *top;
124
125
0
  if ((cur = data = xdl_mmfile_first(mf, &size))) {
126
0
    for (top = data + size; nl < sample && cur < top; ) {
127
0
      nl++;
128
0
      if (!(cur = memchr(cur, '\n', top - cur)))
129
0
        cur = top;
130
0
      else
131
0
        cur++;
132
0
    }
133
0
    tsize += (long) (cur - data);
134
0
  }
135
136
0
  if (nl && tsize)
137
0
    nl = xdl_mmfile_size(mf) / (tsize / nl);
138
139
0
  return nl + 1;
140
0
}
141
142
int xdl_blankline(const char *line, long size, long flags)
143
0
{
144
0
  long i;
145
146
0
  if (!(flags & XDF_WHITESPACE_FLAGS))
147
0
    return (size <= 1);
148
149
0
  for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
150
0
    ;
151
152
0
  return (i == size);
153
0
}
154
155
/*
156
 * Have we eaten everything on the line, except for an optional
157
 * CR at the very end?
158
 */
159
static int ends_with_optional_cr(const char *l, long s, long i)
160
0
{
161
0
  int complete = s && l[s-1] == '\n';
162
163
0
  if (complete)
164
0
    s--;
165
0
  if (s == i)
166
0
    return 1;
167
  /* do not ignore CR at the end of an incomplete line */
168
0
  if (complete && s == i + 1 && l[i] == '\r')
169
0
    return 1;
170
0
  return 0;
171
0
}
172
173
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
174
0
{
175
0
  int i1, i2;
176
177
0
  if (s1 == s2 && !memcmp(l1, l2, s1))
178
0
    return 1;
179
0
  if (!(flags & XDF_WHITESPACE_FLAGS))
180
0
    return 0;
181
182
0
  i1 = 0;
183
0
  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
0
  if (flags & XDF_IGNORE_WHITESPACE) {
194
0
    goto skip_ws;
195
0
    while (i1 < s1 && i2 < s2) {
196
0
      if (l1[i1++] != l2[i2++])
197
0
        return 0;
198
0
    skip_ws:
199
0
      while (i1 < s1 && XDL_ISSPACE(l1[i1]))
200
0
        i1++;
201
0
      while (i2 < s2 && XDL_ISSPACE(l2[i2]))
202
0
        i2++;
203
0
    }
204
0
  } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
205
0
    while (i1 < s1 && i2 < s2) {
206
0
      if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
207
        /* Skip matching spaces and try again */
208
0
        while (i1 < s1 && XDL_ISSPACE(l1[i1]))
209
0
          i1++;
210
0
        while (i2 < s2 && XDL_ISSPACE(l2[i2]))
211
0
          i2++;
212
0
        continue;
213
0
      }
214
0
      if (l1[i1++] != l2[i2++])
215
0
        return 0;
216
0
    }
217
0
  } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
218
0
    while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
219
0
      i1++;
220
0
      i2++;
221
0
    }
222
0
  } else if (flags & XDF_IGNORE_CR_AT_EOL) {
223
    /* Find the first difference and see how the line ends */
224
0
    while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
225
0
      i1++;
226
0
      i2++;
227
0
    }
228
0
    return (ends_with_optional_cr(l1, s1, i1) &&
229
0
      ends_with_optional_cr(l2, s2, i2));
230
0
  }
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
0
  if (i1 < s1) {
239
0
    while (i1 < s1 && XDL_ISSPACE(l1[i1]))
240
0
      i1++;
241
0
    if (s1 != i1)
242
0
      return 0;
243
0
  }
244
0
  if (i2 < s2) {
245
0
    while (i2 < s2 && XDL_ISSPACE(l2[i2]))
246
0
      i2++;
247
0
    return (s2 == i2);
248
0
  }
249
0
  return 1;
250
0
}
251
252
uint64_t xdl_hash_record_with_whitespace(uint8_t const **data,
253
0
    uint8_t const *top, uint64_t flags) {
254
0
  uint64_t ha = 5381;
255
0
  uint8_t const *ptr = *data;
256
0
  bool cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
257
258
0
  for (; ptr < top && *ptr != '\n'; ptr++) {
259
0
    if (cr_at_eol_only) {
260
      /* do not ignore CR at the end of an incomplete line */
261
0
      if (*ptr == '\r' &&
262
0
          (ptr + 1 < top && ptr[1] == '\n'))
263
0
        continue;
264
0
    }
265
0
    else if (XDL_ISSPACE(*ptr)) {
266
0
      const uint8_t *ptr2 = ptr;
267
0
      bool at_eol;
268
0
      while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
269
0
          && ptr[1] != '\n')
270
0
        ptr++;
271
0
      at_eol = (top <= ptr + 1 || ptr[1] == '\n');
272
0
      if (flags & XDF_IGNORE_WHITESPACE)
273
0
        ; /* already handled */
274
0
      else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
275
0
         && !at_eol) {
276
0
        ha += (ha << 5);
277
0
        ha ^= (uint64_t) ' ';
278
0
      }
279
0
      else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
280
0
         && !at_eol) {
281
0
        while (ptr2 != ptr + 1) {
282
0
          ha += (ha << 5);
283
0
          ha ^= (uint64_t) *ptr2;
284
0
          ptr2++;
285
0
        }
286
0
      }
287
0
      continue;
288
0
    }
289
0
    ha += (ha << 5);
290
0
    ha ^= (uint64_t) *ptr;
291
0
  }
292
0
  *data = ptr < top ? ptr + 1: ptr;
293
294
0
  return ha;
295
0
}
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
0
#define REASSOC_FENCE(x, y) __asm__("" : "+r"(x), "+r"(y))
303
#else
304
#define REASSOC_FENCE(x, y)
305
#endif
306
307
0
uint64_t xdl_hash_record_verbatim(uint8_t const **data, uint8_t const *top) {
308
0
  uint64_t ha = 5381, c0, c1;
309
0
  uint8_t 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 += (uint64_t) *ptr;
318
  }
319
  *data = ptr < top ? ptr + 1: ptr;
320
#else
321
  /* Process two characters per iteration. */
322
0
  if (top - ptr >= 2) do {
323
0
    if ((c0 = ptr[0]) == '\n') {
324
0
      *data = ptr + 1;
325
0
      return ha;
326
0
    }
327
0
    if ((c1 = ptr[1]) == '\n') {
328
0
      *data = ptr + 2;
329
0
      c0 += ha;
330
0
      REASSOC_FENCE(c0, ha);
331
0
      ha = ha * 32 + c0;
332
0
      return ha;
333
0
    }
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
0
    ha *= 33 * 33;
343
0
    c1 += c0;
344
0
    REASSOC_FENCE(c1, c0);
345
0
    c1 += c0 * 32;
346
0
    REASSOC_FENCE(c1, ha);
347
0
    ha += c1;
348
349
0
    ptr += 2;
350
0
  } while (ptr < top - 1);
351
0
  *data = top;
352
0
  if (ptr < top && (c0 = ptr[0]) != '\n') {
353
0
    c0 += ha;
354
0
    REASSOC_FENCE(c0, ha);
355
0
    ha = ha * 32 + c0;
356
0
  }
357
0
#endif
358
0
  return ha;
359
0
}
360
361
0
unsigned int xdl_hashbits(unsigned int size) {
362
0
  unsigned int val = 1, bits = 0;
363
364
0
  for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
365
0
  return bits ? bits: 1;
366
0
}
367
368
369
0
int xdl_num_out(char *out, long val) {
370
0
  char *ptr, *str = out;
371
0
  char buf[32];
372
373
0
  ptr = buf + sizeof(buf) - 1;
374
0
  *ptr = '\0';
375
0
  if (val < 0) {
376
0
    *--ptr = '-';
377
0
    val = -val;
378
0
  }
379
0
  for (; val && ptr > buf; val /= 10)
380
0
    *--ptr = "0123456789"[val % 10];
381
0
  if (*ptr)
382
0
    for (; *ptr; ptr++, str++)
383
0
      *str = *ptr;
384
0
  else
385
0
    *str++ = '0';
386
0
  *str = '\0';
387
388
0
  return str - out;
389
0
}
390
391
static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
392
             const char *func, long funclen,
393
0
             xdemitcb_t *ecb) {
394
0
  int nb = 0;
395
0
  mmbuffer_t mb;
396
0
  char buf[128];
397
398
0
  memcpy(buf, "@@ -", 4);
399
0
  nb += 4;
400
401
0
  nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
402
403
0
  if (c1 != 1) {
404
0
    memcpy(buf + nb, ",", 1);
405
0
    nb += 1;
406
407
0
    nb += xdl_num_out(buf + nb, c1);
408
0
  }
409
410
0
  memcpy(buf + nb, " +", 2);
411
0
  nb += 2;
412
413
0
  nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
414
415
0
  if (c2 != 1) {
416
0
    memcpy(buf + nb, ",", 1);
417
0
    nb += 1;
418
419
0
    nb += xdl_num_out(buf + nb, c2);
420
0
  }
421
422
0
  memcpy(buf + nb, " @@", 3);
423
0
  nb += 3;
424
0
  if (func && funclen) {
425
0
    buf[nb++] = ' ';
426
0
    if ((size_t)funclen > sizeof(buf) - nb - 1)
427
0
      funclen = sizeof(buf) - nb - 1;
428
0
    memcpy(buf + nb, func, funclen);
429
0
    nb += funclen;
430
0
  }
431
0
  buf[nb++] = '\n';
432
433
0
  mb.ptr = buf;
434
0
  mb.size = nb;
435
0
  if (ecb->out_line(ecb->priv, &mb, 1) < 0)
436
0
    return -1;
437
0
  return 0;
438
0
}
439
440
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
441
          const char *func, long funclen,
442
0
          xdemitcb_t *ecb) {
443
0
  if (!ecb->out_hunk)
444
0
    return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb);
445
0
  if (ecb->out_hunk(ecb->priv,
446
0
        c1 ? s1 : s1 - 1, c1,
447
0
        c2 ? s2 : s2 - 1, c2,
448
0
        func, funclen) < 0)
449
0
    return -1;
450
0
  return 0;
451
0
}
452
453
int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
454
    int line1, int count1, int line2, int count2)
455
0
{
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
0
  mmfile_t subfile1, subfile2;
465
0
  xdfenv_t env;
466
467
0
  subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;
468
0
  subfile1.size = (char *)diff_env->xdf1.recs[line1 + count1 - 2].ptr +
469
0
    diff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;
470
0
  subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;
471
0
  subfile2.size = (char *)diff_env->xdf2.recs[line2 + count2 - 2].ptr +
472
0
    diff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;
473
0
  if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
474
0
    return -1;
475
476
0
  memcpy(diff_env->xdf1.changed + line1 - 1, env.xdf1.changed, count1);
477
0
  memcpy(diff_env->xdf2.changed + line2 - 1, env.xdf2.changed, count2);
478
479
0
  xdl_free_env(&env);
480
481
0
  return 0;
482
0
}
483
484
void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
485
0
{
486
0
  void *tmp = NULL;
487
0
  size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
488
0
  if ((size_t)nr > n)
489
0
    n = nr;
490
0
  if (SIZE_MAX / size >= n)
491
0
    tmp = xdl_realloc(p, n * size);
492
0
  if (tmp) {
493
0
    *alloc = n;
494
0
  } else {
495
0
    xdl_free(p);
496
0
    *alloc = 0;
497
0
  }
498
0
  return tmp;
499
0
}