1 #include "git-compat-util.h"
7 #include "commit-graph.h"
10 define_commit_slab(bloom_filter_slab
, struct bloom_filter
);
12 static struct bloom_filter_slab bloom_filters
;
14 struct pathmap_hash_entry
{
15 struct hashmap_entry entry
;
16 const char path
[FLEX_ARRAY
];
19 static uint32_t rotate_left(uint32_t value
, int32_t count
)
21 uint32_t mask
= 8 * sizeof(uint32_t) - 1;
23 return ((value
<< count
) | (value
>> ((-count
) & mask
)));
26 static inline unsigned char get_bitmask(uint32_t pos
)
28 return ((unsigned char)1) << (pos
& (BITS_PER_WORD
- 1));
31 static int load_bloom_filter_from_graph(struct commit_graph
*g
,
32 struct bloom_filter
*filter
,
35 uint32_t lex_pos
, start_index
, end_index
;
36 uint32_t graph_pos
= commit_graph_position(c
);
38 while (graph_pos
< g
->num_commits_in_base
)
41 /* The commit graph commit 'c' lives in doesn't carry Bloom filters. */
42 if (!g
->chunk_bloom_indexes
)
45 lex_pos
= graph_pos
- g
->num_commits_in_base
;
47 end_index
= get_be32(g
->chunk_bloom_indexes
+ 4 * lex_pos
);
50 start_index
= get_be32(g
->chunk_bloom_indexes
+ 4 * (lex_pos
- 1));
54 filter
->len
= end_index
- start_index
;
55 filter
->data
= (unsigned char *)(g
->chunk_bloom_data
+
56 sizeof(unsigned char) * start_index
+
57 BLOOMDATA_CHUNK_HEADER_SIZE
);
63 * Calculate the murmur3 32-bit hash value for the given data
64 * using the given seed.
65 * Produces a uniformly distributed hash value.
66 * Not considered to be cryptographically secure.
67 * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
69 uint32_t murmur3_seeded(uint32_t seed
, const char *data
, size_t len
)
71 const uint32_t c1
= 0xcc9e2d51;
72 const uint32_t c2
= 0x1b873593;
73 const uint32_t r1
= 15;
74 const uint32_t r2
= 13;
76 const uint32_t n
= 0xe6546b64;
81 int len4
= len
/ sizeof(uint32_t);
84 for (i
= 0; i
< len4
; i
++) {
85 uint32_t byte1
= (uint32_t)data
[4*i
];
86 uint32_t byte2
= ((uint32_t)data
[4*i
+ 1]) << 8;
87 uint32_t byte3
= ((uint32_t)data
[4*i
+ 2]) << 16;
88 uint32_t byte4
= ((uint32_t)data
[4*i
+ 3]) << 24;
89 k
= byte1
| byte2
| byte3
| byte4
;
91 k
= rotate_left(k
, r1
);
95 seed
= rotate_left(seed
, r2
) * m
+ n
;
98 tail
= (data
+ len4
* sizeof(uint32_t));
100 switch (len
& (sizeof(uint32_t) - 1)) {
102 k1
^= ((uint32_t)tail
[2]) << 16;
105 k1
^= ((uint32_t)tail
[1]) << 8;
108 k1
^= ((uint32_t)tail
[0]) << 0;
110 k1
= rotate_left(k1
, r1
);
116 seed
^= (uint32_t)len
;
117 seed
^= (seed
>> 16);
119 seed
^= (seed
>> 13);
121 seed
^= (seed
>> 16);
126 void fill_bloom_key(const char *data
,
128 struct bloom_key
*key
,
129 const struct bloom_filter_settings
*settings
)
132 const uint32_t seed0
= 0x293ae76f;
133 const uint32_t seed1
= 0x7e646e2c;
134 const uint32_t hash0
= murmur3_seeded(seed0
, data
, len
);
135 const uint32_t hash1
= murmur3_seeded(seed1
, data
, len
);
137 key
->hashes
= (uint32_t *)xcalloc(settings
->num_hashes
, sizeof(uint32_t));
138 for (i
= 0; i
< settings
->num_hashes
; i
++)
139 key
->hashes
[i
] = hash0
+ i
* hash1
;
142 void clear_bloom_key(struct bloom_key
*key
)
144 FREE_AND_NULL(key
->hashes
);
147 void add_key_to_filter(const struct bloom_key
*key
,
148 struct bloom_filter
*filter
,
149 const struct bloom_filter_settings
*settings
)
152 uint64_t mod
= filter
->len
* BITS_PER_WORD
;
154 for (i
= 0; i
< settings
->num_hashes
; i
++) {
155 uint64_t hash_mod
= key
->hashes
[i
] % mod
;
156 uint64_t block_pos
= hash_mod
/ BITS_PER_WORD
;
158 filter
->data
[block_pos
] |= get_bitmask(hash_mod
);
162 void init_bloom_filters(void)
164 init_bloom_filter_slab(&bloom_filters
);
167 static int pathmap_cmp(const void *hashmap_cmp_fn_data
,
168 const struct hashmap_entry
*eptr
,
169 const struct hashmap_entry
*entry_or_key
,
172 const struct pathmap_hash_entry
*e1
, *e2
;
174 e1
= container_of(eptr
, const struct pathmap_hash_entry
, entry
);
175 e2
= container_of(entry_or_key
, const struct pathmap_hash_entry
, entry
);
177 return strcmp(e1
->path
, e2
->path
);
180 static void init_truncated_large_filter(struct bloom_filter
*filter
)
182 filter
->data
= xmalloc(1);
183 filter
->data
[0] = 0xFF;
187 struct bloom_filter
*get_or_compute_bloom_filter(struct repository
*r
,
189 int compute_if_not_present
,
190 const struct bloom_filter_settings
*settings
,
191 enum bloom_filter_computed
*computed
)
193 struct bloom_filter
*filter
;
195 struct diff_options diffopt
;
198 *computed
= BLOOM_NOT_COMPUTED
;
200 if (!bloom_filters
.slab_size
)
203 filter
= bloom_filter_slab_at(&bloom_filters
, c
);
206 load_commit_graph_info(r
, c
);
207 if (commit_graph_position(c
) != COMMIT_NOT_FROM_GRAPH
)
208 load_bloom_filter_from_graph(r
->objects
->commit_graph
, filter
, c
);
211 if (filter
->data
&& filter
->len
)
213 if (!compute_if_not_present
)
216 repo_diff_setup(r
, &diffopt
);
217 diffopt
.flags
.recursive
= 1;
218 diffopt
.detect_rename
= 0;
219 diffopt
.max_changes
= settings
->max_changed_paths
;
220 diff_setup_done(&diffopt
);
222 /* ensure commit is parsed so we have parent information */
223 repo_parse_commit(r
, c
);
226 diff_tree_oid(&c
->parents
->item
->object
.oid
, &c
->object
.oid
, "", &diffopt
);
228 diff_tree_oid(NULL
, &c
->object
.oid
, "", &diffopt
);
229 diffcore_std(&diffopt
);
231 if (diff_queued_diff
.nr
<= settings
->max_changed_paths
) {
232 struct hashmap pathmap
= HASHMAP_INIT(pathmap_cmp
, NULL
);
233 struct pathmap_hash_entry
*e
;
234 struct hashmap_iter iter
;
236 for (i
= 0; i
< diff_queued_diff
.nr
; i
++) {
237 const char *path
= diff_queued_diff
.queue
[i
]->two
->path
;
240 * Add each leading directory of the changed file, i.e. for
241 * 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so
242 * the Bloom filter could be used to speed up commands like
243 * 'git log dir/subdir', too.
245 * Note that directories are added without the trailing '/'.
248 char *last_slash
= strrchr(path
, '/');
250 FLEX_ALLOC_STR(e
, path
, path
);
251 hashmap_entry_init(&e
->entry
, strhash(path
));
253 if (!hashmap_get(&pathmap
, &e
->entry
, NULL
))
254 hashmap_add(&pathmap
, &e
->entry
);
259 last_slash
= (char*)path
;
264 diff_free_filepair(diff_queued_diff
.queue
[i
]);
267 if (hashmap_get_size(&pathmap
) > settings
->max_changed_paths
) {
268 init_truncated_large_filter(filter
);
270 *computed
|= BLOOM_TRUNC_LARGE
;
274 filter
->len
= (hashmap_get_size(&pathmap
) * settings
->bits_per_entry
+ BITS_PER_WORD
- 1) / BITS_PER_WORD
;
277 *computed
|= BLOOM_TRUNC_EMPTY
;
280 CALLOC_ARRAY(filter
->data
, filter
->len
);
282 hashmap_for_each_entry(&pathmap
, &iter
, e
, entry
) {
283 struct bloom_key key
;
284 fill_bloom_key(e
->path
, strlen(e
->path
), &key
, settings
);
285 add_key_to_filter(&key
, filter
, settings
);
289 hashmap_clear_and_free(&pathmap
, struct pathmap_hash_entry
, entry
);
291 for (i
= 0; i
< diff_queued_diff
.nr
; i
++)
292 diff_free_filepair(diff_queued_diff
.queue
[i
]);
293 init_truncated_large_filter(filter
);
296 *computed
|= BLOOM_TRUNC_LARGE
;
300 *computed
|= BLOOM_COMPUTED
;
302 free(diff_queued_diff
.queue
);
303 DIFF_QUEUE_CLEAR(&diff_queued_diff
);
308 int bloom_filter_contains(const struct bloom_filter
*filter
,
309 const struct bloom_key
*key
,
310 const struct bloom_filter_settings
*settings
)
313 uint64_t mod
= filter
->len
* BITS_PER_WORD
;
318 for (i
= 0; i
< settings
->num_hashes
; i
++) {
319 uint64_t hash_mod
= key
->hashes
[i
] % mod
;
320 uint64_t block_pos
= hash_mod
/ BITS_PER_WORD
;
321 if (!(filter
->data
[block_pos
] & get_bitmask(hash_mod
)))