1 #include "git-compat-util.h"
8 define_commit_slab(bloom_filter_slab
, struct bloom_filter
);
10 struct bloom_filter_slab bloom_filters
;
12 struct pathmap_hash_entry
{
13 struct hashmap_entry entry
;
14 const char path
[FLEX_ARRAY
];
17 static uint32_t rotate_left(uint32_t value
, int32_t count
)
19 uint32_t mask
= 8 * sizeof(uint32_t) - 1;
21 return ((value
<< count
) | (value
>> ((-count
) & mask
)));
24 static inline unsigned char get_bitmask(uint32_t pos
)
26 return ((unsigned char)1) << (pos
& (BITS_PER_WORD
- 1));
30 * Calculate the murmur3 32-bit hash value for the given data
31 * using the given seed.
32 * Produces a uniformly distributed hash value.
33 * Not considered to be cryptographically secure.
34 * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
36 uint32_t murmur3_seeded(uint32_t seed
, const char *data
, size_t len
)
38 const uint32_t c1
= 0xcc9e2d51;
39 const uint32_t c2
= 0x1b873593;
40 const uint32_t r1
= 15;
41 const uint32_t r2
= 13;
43 const uint32_t n
= 0xe6546b64;
48 int len4
= len
/ sizeof(uint32_t);
51 for (i
= 0; i
< len4
; i
++) {
52 uint32_t byte1
= (uint32_t)data
[4*i
];
53 uint32_t byte2
= ((uint32_t)data
[4*i
+ 1]) << 8;
54 uint32_t byte3
= ((uint32_t)data
[4*i
+ 2]) << 16;
55 uint32_t byte4
= ((uint32_t)data
[4*i
+ 3]) << 24;
56 k
= byte1
| byte2
| byte3
| byte4
;
58 k
= rotate_left(k
, r1
);
62 seed
= rotate_left(seed
, r2
) * m
+ n
;
65 tail
= (data
+ len4
* sizeof(uint32_t));
67 switch (len
& (sizeof(uint32_t) - 1)) {
69 k1
^= ((uint32_t)tail
[2]) << 16;
72 k1
^= ((uint32_t)tail
[1]) << 8;
75 k1
^= ((uint32_t)tail
[0]) << 0;
77 k1
= rotate_left(k1
, r1
);
83 seed
^= (uint32_t)len
;
93 void fill_bloom_key(const char *data
,
95 struct bloom_key
*key
,
96 const struct bloom_filter_settings
*settings
)
99 const uint32_t seed0
= 0x293ae76f;
100 const uint32_t seed1
= 0x7e646e2c;
101 const uint32_t hash0
= murmur3_seeded(seed0
, data
, len
);
102 const uint32_t hash1
= murmur3_seeded(seed1
, data
, len
);
104 key
->hashes
= (uint32_t *)xcalloc(settings
->num_hashes
, sizeof(uint32_t));
105 for (i
= 0; i
< settings
->num_hashes
; i
++)
106 key
->hashes
[i
] = hash0
+ i
* hash1
;
109 void add_key_to_filter(const struct bloom_key
*key
,
110 struct bloom_filter
*filter
,
111 const struct bloom_filter_settings
*settings
)
114 uint64_t mod
= filter
->len
* BITS_PER_WORD
;
116 for (i
= 0; i
< settings
->num_hashes
; i
++) {
117 uint64_t hash_mod
= key
->hashes
[i
] % mod
;
118 uint64_t block_pos
= hash_mod
/ BITS_PER_WORD
;
120 filter
->data
[block_pos
] |= get_bitmask(hash_mod
);
124 void init_bloom_filters(void)
126 init_bloom_filter_slab(&bloom_filters
);
129 struct bloom_filter
*get_bloom_filter(struct repository
*r
,
132 struct bloom_filter
*filter
;
133 struct bloom_filter_settings settings
= DEFAULT_BLOOM_FILTER_SETTINGS
;
135 struct diff_options diffopt
;
136 int max_changes
= 512;
138 if (bloom_filters
.slab_size
== 0)
141 filter
= bloom_filter_slab_at(&bloom_filters
, c
);
143 repo_diff_setup(r
, &diffopt
);
144 diffopt
.flags
.recursive
= 1;
145 diffopt
.max_changes
= max_changes
;
146 diff_setup_done(&diffopt
);
149 diff_tree_oid(&c
->parents
->item
->object
.oid
, &c
->object
.oid
, "", &diffopt
);
151 diff_tree_oid(NULL
, &c
->object
.oid
, "", &diffopt
);
152 diffcore_std(&diffopt
);
154 if (diff_queued_diff
.nr
<= max_changes
) {
155 struct hashmap pathmap
;
156 struct pathmap_hash_entry
*e
;
157 struct hashmap_iter iter
;
158 hashmap_init(&pathmap
, NULL
, NULL
, 0);
160 for (i
= 0; i
< diff_queued_diff
.nr
; i
++) {
161 const char *path
= diff_queued_diff
.queue
[i
]->two
->path
;
164 * Add each leading directory of the changed file, i.e. for
165 * 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so
166 * the Bloom filter could be used to speed up commands like
167 * 'git log dir/subdir', too.
169 * Note that directories are added without the trailing '/'.
172 char *last_slash
= strrchr(path
, '/');
174 FLEX_ALLOC_STR(e
, path
, path
);
175 hashmap_entry_init(&e
->entry
, strhash(path
));
176 hashmap_add(&pathmap
, &e
->entry
);
179 last_slash
= (char*)path
;
184 diff_free_filepair(diff_queued_diff
.queue
[i
]);
187 filter
->len
= (hashmap_get_size(&pathmap
) * settings
.bits_per_entry
+ BITS_PER_WORD
- 1) / BITS_PER_WORD
;
188 filter
->data
= xcalloc(filter
->len
, sizeof(unsigned char));
190 hashmap_for_each_entry(&pathmap
, &iter
, e
, entry
) {
191 struct bloom_key key
;
192 fill_bloom_key(e
->path
, strlen(e
->path
), &key
, &settings
);
193 add_key_to_filter(&key
, filter
, &settings
);
196 hashmap_free_entries(&pathmap
, struct pathmap_hash_entry
, entry
);
198 for (i
= 0; i
< diff_queued_diff
.nr
; i
++)
199 diff_free_filepair(diff_queued_diff
.queue
[i
]);
204 free(diff_queued_diff
.queue
);
205 DIFF_QUEUE_CLEAR(&diff_queued_diff
);