|
1 | 1 | #include "git-compat-util.h" |
2 | 2 | #include "bloom.h" |
| 3 | +#include "diff.h" |
| 4 | +#include "diffcore.h" |
| 5 | +#include "revision.h" |
| 6 | +#include "hashmap.h" |
| 7 | + |
| 8 | +define_commit_slab(bloom_filter_slab, struct bloom_filter); |
| 9 | + |
| 10 | +struct bloom_filter_slab bloom_filters; |
| 11 | + |
| 12 | +struct pathmap_hash_entry { |
| 13 | + struct hashmap_entry entry; |
| 14 | + const char path[FLEX_ARRAY]; |
| 15 | +}; |
3 | 16 |
|
4 | 17 | static uint32_t rotate_left(uint32_t value, int32_t count) |
5 | 18 | { |
@@ -107,3 +120,87 @@ void add_key_to_filter(const struct bloom_key *key, |
107 | 120 | filter->data[block_pos] |= get_bitmask(hash_mod); |
108 | 121 | } |
109 | 122 | } |
| 123 | + |
| 124 | +void init_bloom_filters(void) |
| 125 | +{ |
| 126 | + init_bloom_filter_slab(&bloom_filters); |
| 127 | +} |
| 128 | + |
| 129 | +struct bloom_filter *get_bloom_filter(struct repository *r, |
| 130 | + struct commit *c) |
| 131 | +{ |
| 132 | + struct bloom_filter *filter; |
| 133 | + struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS; |
| 134 | + int i; |
| 135 | + struct diff_options diffopt; |
| 136 | + |
| 137 | + if (bloom_filters.slab_size == 0) |
| 138 | + return NULL; |
| 139 | + |
| 140 | + filter = bloom_filter_slab_at(&bloom_filters, c); |
| 141 | + |
| 142 | + repo_diff_setup(r, &diffopt); |
| 143 | + diffopt.flags.recursive = 1; |
| 144 | + diff_setup_done(&diffopt); |
| 145 | + |
| 146 | + if (c->parents) |
| 147 | + diff_tree_oid(&c->parents->item->object.oid, &c->object.oid, "", &diffopt); |
| 148 | + else |
| 149 | + diff_tree_oid(NULL, &c->object.oid, "", &diffopt); |
| 150 | + diffcore_std(&diffopt); |
| 151 | + |
| 152 | + if (diff_queued_diff.nr <= 512) { |
| 153 | + struct hashmap pathmap; |
| 154 | + struct pathmap_hash_entry *e; |
| 155 | + struct hashmap_iter iter; |
| 156 | + hashmap_init(&pathmap, NULL, NULL, 0); |
| 157 | + |
| 158 | + for (i = 0; i < diff_queued_diff.nr; i++) { |
| 159 | + const char *path = diff_queued_diff.queue[i]->two->path; |
| 160 | + |
| 161 | + /* |
| 162 | + * Add each leading directory of the changed file, i.e. for |
| 163 | + * 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so |
| 164 | + * the Bloom filter could be used to speed up commands like |
| 165 | + * 'git log dir/subdir', too. |
| 166 | + * |
| 167 | + * Note that directories are added without the trailing '/'. |
| 168 | + */ |
| 169 | + do { |
| 170 | + char *last_slash = strrchr(path, '/'); |
| 171 | + |
| 172 | + FLEX_ALLOC_STR(e, path, path); |
| 173 | + hashmap_entry_init(&e->entry, strhash(path)); |
| 174 | + hashmap_add(&pathmap, &e->entry); |
| 175 | + |
| 176 | + if (!last_slash) |
| 177 | + last_slash = (char*)path; |
| 178 | + *last_slash = '\0'; |
| 179 | + |
| 180 | + } while (*path); |
| 181 | + |
| 182 | + diff_free_filepair(diff_queued_diff.queue[i]); |
| 183 | + } |
| 184 | + |
| 185 | + filter->len = (hashmap_get_size(&pathmap) * settings.bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD; |
| 186 | + filter->data = xcalloc(filter->len, sizeof(unsigned char)); |
| 187 | + |
| 188 | + hashmap_for_each_entry(&pathmap, &iter, e, entry) { |
| 189 | + struct bloom_key key; |
| 190 | + fill_bloom_key(e->path, strlen(e->path), &key, &settings); |
| 191 | + add_key_to_filter(&key, filter, &settings); |
| 192 | + } |
| 193 | + |
| 194 | + hashmap_free_entries(&pathmap, struct pathmap_hash_entry, entry); |
| 195 | + } else { |
| 196 | + for (i = 0; i < diff_queued_diff.nr; i++) |
| 197 | + diff_free_filepair(diff_queued_diff.queue[i]); |
| 198 | + filter->data = NULL; |
| 199 | + filter->len = 0; |
| 200 | + } |
| 201 | + |
| 202 | + free(diff_queued_diff.queue); |
| 203 | + DIFF_QUEUE_CLEAR(&diff_queued_diff); |
| 204 | + |
| 205 | + return filter; |
| 206 | +} |
0 commit comments