Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/bloom.c
Line
Count
Source
1
#define DISABLE_SIGN_COMPARE_WARNINGS
2
3
#include "git-compat-util.h"
4
#include "bloom.h"
5
#include "diff.h"
6
#include "diffcore.h"
7
#include "hashmap.h"
8
#include "commit-graph.h"
9
#include "commit.h"
10
#include "commit-slab.h"
11
#include "tree.h"
12
#include "tree-walk.h"
13
#include "config.h"
14
#include "repository.h"
15
16
0
define_commit_slab(bloom_filter_slab, struct bloom_filter);
Unexecuted instantiation: bloom.c:init_bloom_filter_slab_with_stride
Unexecuted instantiation: bloom.c:clear_bloom_filter_slab
Unexecuted instantiation: bloom.c:bloom_filter_slab_at_peek
17
0
18
0
static struct bloom_filter_slab bloom_filters;
19
0
static int bloom_filter_slab_initialized;
20
0
21
0
struct pathmap_hash_entry {
22
0
    struct hashmap_entry entry;
23
0
    const char path[FLEX_ARRAY];
24
0
};
25
0
26
0
static uint32_t rotate_left(uint32_t value, int32_t count)
27
0
{
28
0
  uint32_t mask = 8 * sizeof(uint32_t) - 1;
29
0
  count &= mask;
30
0
  return ((value << count) | (value >> ((-count) & mask)));
31
0
}
32
33
static inline unsigned char get_bitmask(uint32_t pos)
34
0
{
35
0
  return ((unsigned char)1) << (pos & (BITS_PER_WORD - 1));
36
0
}
37
38
static int check_bloom_offset(struct commit_graph *g, uint32_t pos,
39
            uint32_t offset)
40
0
{
41
  /*
42
   * Note that we allow offsets equal to the data size, which would set
43
   * our pointers at one past the end of the chunk memory. This is
44
   * necessary because the on-disk index points to the end of the
45
   * entries (so we can compute size by comparing adjacent ones). And
46
   * naturally the final entry's end is one-past-the-end of the chunk.
47
   */
48
0
  if (offset <= g->chunk_bloom_data_size - BLOOMDATA_CHUNK_HEADER_SIZE)
49
0
    return 0;
50
51
0
  warning("ignoring out-of-range offset (%"PRIuMAX") for changed-path"
52
0
    " filter at pos %"PRIuMAX" of %s (chunk size: %"PRIuMAX")",
53
0
    (uintmax_t)offset, (uintmax_t)pos,
54
0
    g->filename, (uintmax_t)g->chunk_bloom_data_size);
55
0
  return -1;
56
0
}
57
58
int load_bloom_filter_from_graph(struct commit_graph *g,
59
         struct bloom_filter *filter,
60
         uint32_t graph_pos)
61
0
{
62
0
  uint32_t lex_pos, start_index, end_index;
63
64
0
  while (graph_pos < g->num_commits_in_base)
65
0
    g = g->base_graph;
66
67
  /* The commit graph commit 'c' lives in doesn't carry Bloom filters. */
68
0
  if (!g->chunk_bloom_indexes)
69
0
    return 0;
70
71
0
  lex_pos = graph_pos - g->num_commits_in_base;
72
73
0
  end_index = get_be32(g->chunk_bloom_indexes + 4 * lex_pos);
74
75
0
  if (lex_pos > 0)
76
0
    start_index = get_be32(g->chunk_bloom_indexes + 4 * (lex_pos - 1));
77
0
  else
78
0
    start_index = 0;
79
80
0
  if (check_bloom_offset(g, lex_pos, end_index) < 0 ||
81
0
      check_bloom_offset(g, lex_pos - 1, start_index) < 0)
82
0
    return 0;
83
84
0
  if (end_index < start_index) {
85
0
    warning("ignoring decreasing changed-path index offsets"
86
0
      " (%"PRIuMAX" > %"PRIuMAX") for positions"
87
0
      " %"PRIuMAX" and %"PRIuMAX" of %s",
88
0
      (uintmax_t)start_index, (uintmax_t)end_index,
89
0
      (uintmax_t)(lex_pos-1), (uintmax_t)lex_pos,
90
0
      g->filename);
91
0
    return 0;
92
0
  }
93
94
0
  filter->len = end_index - start_index;
95
0
  filter->data = (unsigned char *)(g->chunk_bloom_data +
96
0
          sizeof(unsigned char) * start_index +
97
0
          BLOOMDATA_CHUNK_HEADER_SIZE);
98
0
  filter->version = g->bloom_filter_settings->hash_version;
99
0
  filter->to_free = NULL;
100
101
0
  return 1;
102
0
}
103
104
/*
105
 * Calculate the murmur3 32-bit hash value for the given data
106
 * using the given seed.
107
 * Produces a uniformly distributed hash value.
108
 * Not considered to be cryptographically secure.
109
 * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
110
 */
111
static uint32_t murmur3_seeded_v2(uint32_t seed, const char *data, size_t len)
112
0
{
113
0
  const uint32_t c1 = 0xcc9e2d51;
114
0
  const uint32_t c2 = 0x1b873593;
115
0
  const uint32_t r1 = 15;
116
0
  const uint32_t r2 = 13;
117
0
  const uint32_t m = 5;
118
0
  const uint32_t n = 0xe6546b64;
119
0
  int i;
120
0
  uint32_t k1 = 0;
121
0
  const char *tail;
122
123
0
  int len4 = len / sizeof(uint32_t);
124
125
0
  uint32_t k;
126
0
  for (i = 0; i < len4; i++) {
127
0
    uint32_t byte1 = (uint32_t)(unsigned char)data[4*i];
128
0
    uint32_t byte2 = ((uint32_t)(unsigned char)data[4*i + 1]) << 8;
129
0
    uint32_t byte3 = ((uint32_t)(unsigned char)data[4*i + 2]) << 16;
130
0
    uint32_t byte4 = ((uint32_t)(unsigned char)data[4*i + 3]) << 24;
131
0
    k = byte1 | byte2 | byte3 | byte4;
132
0
    k *= c1;
133
0
    k = rotate_left(k, r1);
134
0
    k *= c2;
135
136
0
    seed ^= k;
137
0
    seed = rotate_left(seed, r2) * m + n;
138
0
  }
139
140
0
  tail = (data + len4 * sizeof(uint32_t));
141
142
0
  switch (len & (sizeof(uint32_t) - 1)) {
143
0
  case 3:
144
0
    k1 ^= ((uint32_t)(unsigned char)tail[2]) << 16;
145
    /*-fallthrough*/
146
0
  case 2:
147
0
    k1 ^= ((uint32_t)(unsigned char)tail[1]) << 8;
148
    /*-fallthrough*/
149
0
  case 1:
150
0
    k1 ^= ((uint32_t)(unsigned char)tail[0]) << 0;
151
0
    k1 *= c1;
152
0
    k1 = rotate_left(k1, r1);
153
0
    k1 *= c2;
154
0
    seed ^= k1;
155
0
    break;
156
0
  }
157
158
0
  seed ^= (uint32_t)len;
159
0
  seed ^= (seed >> 16);
160
0
  seed *= 0x85ebca6b;
161
0
  seed ^= (seed >> 13);
162
0
  seed *= 0xc2b2ae35;
163
0
  seed ^= (seed >> 16);
164
165
0
  return seed;
166
0
}
167
168
static uint32_t murmur3_seeded_v1(uint32_t seed, const char *data, size_t len)
169
0
{
170
0
  const uint32_t c1 = 0xcc9e2d51;
171
0
  const uint32_t c2 = 0x1b873593;
172
0
  const uint32_t r1 = 15;
173
0
  const uint32_t r2 = 13;
174
0
  const uint32_t m = 5;
175
0
  const uint32_t n = 0xe6546b64;
176
0
  int i;
177
0
  uint32_t k1 = 0;
178
0
  const char *tail;
179
180
0
  int len4 = len / sizeof(uint32_t);
181
182
0
  uint32_t k;
183
0
  for (i = 0; i < len4; i++) {
184
0
    uint32_t byte1 = (uint32_t)data[4*i];
185
0
    uint32_t byte2 = ((uint32_t)data[4*i + 1]) << 8;
186
0
    uint32_t byte3 = ((uint32_t)data[4*i + 2]) << 16;
187
0
    uint32_t byte4 = ((uint32_t)data[4*i + 3]) << 24;
188
0
    k = byte1 | byte2 | byte3 | byte4;
189
0
    k *= c1;
190
0
    k = rotate_left(k, r1);
191
0
    k *= c2;
192
193
0
    seed ^= k;
194
0
    seed = rotate_left(seed, r2) * m + n;
195
0
  }
196
197
0
  tail = (data + len4 * sizeof(uint32_t));
198
199
0
  switch (len & (sizeof(uint32_t) - 1)) {
200
0
  case 3:
201
0
    k1 ^= ((uint32_t)tail[2]) << 16;
202
    /*-fallthrough*/
203
0
  case 2:
204
0
    k1 ^= ((uint32_t)tail[1]) << 8;
205
    /*-fallthrough*/
206
0
  case 1:
207
0
    k1 ^= ((uint32_t)tail[0]) << 0;
208
0
    k1 *= c1;
209
0
    k1 = rotate_left(k1, r1);
210
0
    k1 *= c2;
211
0
    seed ^= k1;
212
0
    break;
213
0
  }
214
215
0
  seed ^= (uint32_t)len;
216
0
  seed ^= (seed >> 16);
217
0
  seed *= 0x85ebca6b;
218
0
  seed ^= (seed >> 13);
219
0
  seed *= 0xc2b2ae35;
220
0
  seed ^= (seed >> 16);
221
222
0
  return seed;
223
0
}
224
225
void bloom_key_fill(struct bloom_key *key, const char *data, size_t len,
226
        const struct bloom_filter_settings *settings)
227
0
{
228
0
  int i;
229
0
  const uint32_t seed0 = 0x293ae76f;
230
0
  const uint32_t seed1 = 0x7e646e2c;
231
0
  uint32_t hash0, hash1;
232
0
  if (settings->hash_version == 2) {
233
0
    hash0 = murmur3_seeded_v2(seed0, data, len);
234
0
    hash1 = murmur3_seeded_v2(seed1, data, len);
235
0
  } else {
236
0
    hash0 = murmur3_seeded_v1(seed0, data, len);
237
0
    hash1 = murmur3_seeded_v1(seed1, data, len);
238
0
  }
239
240
0
  key->hashes = (uint32_t *)xcalloc(settings->num_hashes, sizeof(uint32_t));
241
0
  for (i = 0; i < settings->num_hashes; i++)
242
0
    key->hashes[i] = hash0 + i * hash1;
243
0
}
244
245
void bloom_key_clear(struct bloom_key *key)
246
0
{
247
0
  FREE_AND_NULL(key->hashes);
248
0
}
249
250
void add_key_to_filter(const struct bloom_key *key,
251
           struct bloom_filter *filter,
252
           const struct bloom_filter_settings *settings)
253
0
{
254
0
  int i;
255
0
  uint64_t mod = filter->len * BITS_PER_WORD;
256
257
0
  for (i = 0; i < settings->num_hashes; i++) {
258
0
    uint64_t hash_mod = key->hashes[i] % mod;
259
0
    uint64_t block_pos = hash_mod / BITS_PER_WORD;
260
261
0
    filter->data[block_pos] |= get_bitmask(hash_mod);
262
0
  }
263
0
}
264
265
void init_bloom_filters(void)
266
0
{
267
0
  if (bloom_filter_slab_initialized)
268
0
    return;
269
0
  init_bloom_filter_slab(&bloom_filters);
270
0
  bloom_filter_slab_initialized = 1;
271
0
}
272
273
static void free_one_bloom_filter(struct bloom_filter *filter)
274
0
{
275
0
  if (!filter)
276
0
    return;
277
0
  free(filter->to_free);
278
0
}
279
280
void deinit_bloom_filters(void)
281
0
{
282
0
  deep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter);
283
0
  bloom_filter_slab_initialized = 0;
284
0
}
285
286
struct bloom_keyvec *bloom_keyvec_new(const char *path, size_t len,
287
              const struct bloom_filter_settings *settings)
288
0
{
289
0
  struct bloom_keyvec *vec;
290
0
  const char *p;
291
0
  size_t sz;
292
0
  size_t nr = 1;
293
294
0
  p = path;
295
0
  while (*p) {
296
    /*
297
     * At this point, the path is normalized to use Unix-style
298
     * path separators. This is required due to how the
299
     * changed-path Bloom filters store the paths.
300
     */
301
0
    if (*p == '/')
302
0
      nr++;
303
0
    p++;
304
0
  }
305
306
0
  sz = sizeof(struct bloom_keyvec);
307
0
  sz += nr * sizeof(struct bloom_key);
308
0
  vec = (struct bloom_keyvec *)xcalloc(1, sz);
309
0
  if (!vec)
310
0
    return NULL;
311
0
  vec->count = nr;
312
313
0
  bloom_key_fill(&vec->key[0], path, len, settings);
314
0
  nr = 1;
315
0
  p = path + len - 1;
316
0
  while (p > path) {
317
0
    if (*p == '/') {
318
0
      bloom_key_fill(&vec->key[nr++], path, p - path, settings);
319
0
    }
320
0
    p--;
321
0
  }
322
0
  assert(nr == vec->count);
323
0
  return vec;
324
0
}
325
326
void bloom_keyvec_free(struct bloom_keyvec *vec)
327
0
{
328
0
  if (!vec)
329
0
    return;
330
0
  for (size_t nr = 0; nr < vec->count; nr++)
331
0
    bloom_key_clear(&vec->key[nr]);
332
0
  free(vec);
333
0
}
334
335
static int pathmap_cmp(const void *hashmap_cmp_fn_data UNUSED,
336
           const struct hashmap_entry *eptr,
337
           const struct hashmap_entry *entry_or_key,
338
           const void *keydata UNUSED)
339
0
{
340
0
  const struct pathmap_hash_entry *e1, *e2;
341
342
0
  e1 = container_of(eptr, const struct pathmap_hash_entry, entry);
343
0
  e2 = container_of(entry_or_key, const struct pathmap_hash_entry, entry);
344
345
0
  return strcmp(e1->path, e2->path);
346
0
}
347
348
static void init_truncated_large_filter(struct bloom_filter *filter,
349
          int version)
350
0
{
351
0
  filter->data = filter->to_free = xmalloc(1);
352
0
  filter->data[0] = 0xFF;
353
0
  filter->len = 1;
354
0
  filter->version = version;
355
0
}
356
357
0
#define VISITED   (1u<<21)
358
0
#define HIGH_BITS (1u<<22)
359
360
static int has_entries_with_high_bit(struct repository *r, struct tree *t)
361
0
{
362
0
  if (repo_parse_tree(r, t))
363
0
    return 1;
364
365
0
  if (!(t->object.flags & VISITED)) {
366
0
    struct tree_desc desc;
367
0
    struct name_entry entry;
368
369
0
    init_tree_desc(&desc, &t->object.oid, t->buffer, t->size);
370
0
    while (tree_entry(&desc, &entry)) {
371
0
      size_t i;
372
0
      for (i = 0; i < entry.pathlen; i++) {
373
0
        if (entry.path[i] & 0x80) {
374
0
          t->object.flags |= HIGH_BITS;
375
0
          goto done;
376
0
        }
377
0
      }
378
379
0
      if (S_ISDIR(entry.mode)) {
380
0
        struct tree *sub = lookup_tree(r, &entry.oid);
381
0
        if (sub && has_entries_with_high_bit(r, sub)) {
382
0
          t->object.flags |= HIGH_BITS;
383
0
          goto done;
384
0
        }
385
0
      }
386
387
0
    }
388
389
0
done:
390
0
    t->object.flags |= VISITED;
391
0
  }
392
393
0
  return !!(t->object.flags & HIGH_BITS);
394
0
}
395
396
static int commit_tree_has_high_bit_paths(struct repository *r,
397
            struct commit *c)
398
0
{
399
0
  struct tree *t;
400
0
  if (repo_parse_commit(r, c))
401
0
    return 1;
402
0
  t = repo_get_commit_tree(r, c);
403
0
  if (!t)
404
0
    return 1;
405
0
  return has_entries_with_high_bit(r, t);
406
0
}
407
408
static struct bloom_filter *upgrade_filter(struct repository *r, struct commit *c,
409
             struct bloom_filter *filter,
410
             int hash_version)
411
0
{
412
0
  struct commit_list *p = c->parents;
413
0
  if (commit_tree_has_high_bit_paths(r, c))
414
0
    return NULL;
415
416
0
  if (p && commit_tree_has_high_bit_paths(r, p->item))
417
0
    return NULL;
418
419
0
  filter->version = hash_version;
420
421
0
  return filter;
422
0
}
423
424
struct bloom_filter *get_bloom_filter(struct repository *r, struct commit *c)
425
0
{
426
0
  struct bloom_filter *filter;
427
0
  int hash_version;
428
429
0
  filter = get_or_compute_bloom_filter(r, c, 0, NULL, NULL);
430
0
  if (!filter)
431
0
    return NULL;
432
433
0
  prepare_repo_settings(r);
434
0
  hash_version = r->settings.commit_graph_changed_paths_version;
435
436
0
  if (!(hash_version == -1 || hash_version == filter->version))
437
0
    return NULL; /* unusable filter */
438
0
  return filter;
439
0
}
440
441
struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
442
             struct commit *c,
443
             int compute_if_not_present,
444
             const struct bloom_filter_settings *settings,
445
             enum bloom_filter_computed *computed)
446
0
{
447
0
  struct bloom_filter *filter;
448
0
  int i;
449
0
  struct diff_options diffopt;
450
451
0
  if (computed)
452
0
    *computed = BLOOM_NOT_COMPUTED;
453
454
0
  if (!bloom_filters.slab_size)
455
0
    return NULL;
456
457
0
  filter = bloom_filter_slab_at(&bloom_filters, c);
458
459
0
  if (!filter->data) {
460
0
    struct commit_graph *g;
461
0
    uint32_t graph_pos;
462
463
0
    g = repo_find_commit_pos_in_graph(r, c, &graph_pos);
464
0
    if (g)
465
0
      load_bloom_filter_from_graph(g, filter, graph_pos);
466
0
  }
467
468
0
  if (filter->data && filter->len) {
469
0
    struct bloom_filter *upgrade;
470
0
    if (!settings || settings->hash_version == filter->version)
471
0
      return filter;
472
473
    /* version mismatch, see if we can upgrade */
474
0
    if (compute_if_not_present &&
475
0
        git_env_bool("GIT_TEST_UPGRADE_BLOOM_FILTERS", 1)) {
476
0
      upgrade = upgrade_filter(r, c, filter,
477
0
             settings->hash_version);
478
0
      if (upgrade) {
479
0
        if (computed)
480
0
          *computed |= BLOOM_UPGRADED;
481
0
        return upgrade;
482
0
      }
483
0
    }
484
0
  }
485
0
  if (!compute_if_not_present)
486
0
    return NULL;
487
488
0
  repo_diff_setup(r, &diffopt);
489
0
  diffopt.flags.recursive = 1;
490
0
  diffopt.detect_rename = 0;
491
0
  diffopt.max_changes = settings->max_changed_paths;
492
0
  diff_setup_done(&diffopt);
493
494
  /* ensure commit is parsed so we have parent information */
495
0
  repo_parse_commit(r, c);
496
497
0
  if (c->parents)
498
0
    diff_tree_oid(&c->parents->item->object.oid, &c->object.oid, "", &diffopt);
499
0
  else
500
0
    diff_tree_oid(NULL, &c->object.oid, "", &diffopt);
501
0
  diffcore_std(&diffopt);
502
503
0
  if (diff_queued_diff.nr <= settings->max_changed_paths) {
504
0
    struct hashmap pathmap = HASHMAP_INIT(pathmap_cmp, NULL);
505
0
    struct pathmap_hash_entry *e;
506
0
    struct hashmap_iter iter;
507
508
0
    for (i = 0; i < diff_queued_diff.nr; i++) {
509
0
      char *path = diff_queued_diff.queue[i]->two->path;
510
511
      /*
512
       * Add each leading directory of the changed file, i.e. for
513
       * 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so
514
       * the Bloom filter could be used to speed up commands like
515
       * 'git log dir/subdir', too.
516
       *
517
       * Note that directories are added without the trailing '/'.
518
       */
519
0
      do {
520
0
        char *last_slash = strrchr(path, '/');
521
522
0
        FLEX_ALLOC_STR(e, path, path);
523
0
        hashmap_entry_init(&e->entry, strhash(path));
524
525
0
        if (!hashmap_get(&pathmap, &e->entry, NULL))
526
0
          hashmap_add(&pathmap, &e->entry);
527
0
        else
528
0
          free(e);
529
530
0
        if (!last_slash)
531
0
          last_slash = path;
532
0
        *last_slash = '\0';
533
534
0
      } while (*path);
535
0
    }
536
537
0
    if (hashmap_get_size(&pathmap) > settings->max_changed_paths) {
538
0
      init_truncated_large_filter(filter,
539
0
                settings->hash_version);
540
0
      if (computed)
541
0
        *computed |= BLOOM_TRUNC_LARGE;
542
0
      goto cleanup;
543
0
    }
544
545
0
    filter->len = (hashmap_get_size(&pathmap) * settings->bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD;
546
0
    filter->version = settings->hash_version;
547
0
    if (!filter->len) {
548
0
      if (computed)
549
0
        *computed |= BLOOM_TRUNC_EMPTY;
550
0
      filter->len = 1;
551
0
    }
552
0
    CALLOC_ARRAY(filter->data, filter->len);
553
0
    filter->to_free = filter->data;
554
555
0
    hashmap_for_each_entry(&pathmap, &iter, e, entry) {
556
0
      struct bloom_key key;
557
0
      bloom_key_fill(&key, e->path, strlen(e->path), settings);
558
0
      add_key_to_filter(&key, filter, settings);
559
0
      bloom_key_clear(&key);
560
0
    }
561
562
0
  cleanup:
563
0
    hashmap_clear_and_free(&pathmap, struct pathmap_hash_entry, entry);
564
0
  } else {
565
0
    init_truncated_large_filter(filter, settings->hash_version);
566
567
0
    if (computed)
568
0
      *computed |= BLOOM_TRUNC_LARGE;
569
0
  }
570
571
0
  if (computed)
572
0
    *computed |= BLOOM_COMPUTED;
573
574
0
  diff_queue_clear(&diff_queued_diff);
575
0
  return filter;
576
0
}
577
578
int bloom_filter_contains(const struct bloom_filter *filter,
579
        const struct bloom_key *key,
580
        const struct bloom_filter_settings *settings)
581
0
{
582
0
  int i;
583
0
  uint64_t mod = filter->len * BITS_PER_WORD;
584
585
0
  if (!mod)
586
0
    return -1;
587
588
0
  for (i = 0; i < settings->num_hashes; i++) {
589
0
    uint64_t hash_mod = key->hashes[i] % mod;
590
0
    uint64_t block_pos = hash_mod / BITS_PER_WORD;
591
0
    if (!(filter->data[block_pos] & get_bitmask(hash_mod)))
592
0
      return 0;
593
0
  }
594
595
0
  return 1;
596
0
}
597
598
int bloom_filter_contains_vec(const struct bloom_filter *filter,
599
            const struct bloom_keyvec *vec,
600
            const struct bloom_filter_settings *settings)
601
0
{
602
0
  int ret = 1;
603
604
0
  for (size_t nr = 0; ret > 0 && nr < vec->count; nr++)
605
0
    ret = bloom_filter_contains(filter, &vec->key[nr], settings);
606
607
0
  return ret;
608
0
}
609
610
uint32_t test_bloom_murmur3_seeded(uint32_t seed, const char *data, size_t len,
611
           int version)
612
0
{
613
0
  if (version == 2)
614
0
    return murmur3_seeded_v2(seed, data, len);
615
0
  else if (version == 1)
616
0
    return murmur3_seeded_v1(seed, data, len);
617
0
  else
618
0
    BUG("unexpected bloom version: %d", version);
619
0
}