#include "cache.h" #include "object.h" int track_object_refs = 0; static unsigned int refs_hash_size, nr_object_refs; static struct object_refs **refs_hash; static unsigned int hash_obj(struct object *obj, unsigned int n) { unsigned int hash = *(unsigned int *)obj->sha1; return hash % n; } static void insert_ref_hash(struct object_refs *ref, struct object_refs **hash, unsigned int size) { int j = hash_obj(ref->base, size); while (hash[j]) { j++; if (j >= size) j = 0; } hash[j] = ref; } static void grow_refs_hash(void) { int i; int new_hash_size = (refs_hash_size + 1000) * 3 / 2; struct object_refs **new_hash; new_hash = xcalloc(new_hash_size, sizeof(struct object_refs *)); for (i = 0; i < refs_hash_size; i++) { struct object_refs *ref = refs_hash[i]; if (!ref) continue; insert_ref_hash(ref, new_hash, new_hash_size); } free(refs_hash); refs_hash = new_hash; refs_hash_size = new_hash_size; } static void add_object_refs(struct object *obj, struct object_refs *ref) { int nr = nr_object_refs + 1; if (nr > refs_hash_size * 2 / 3) grow_refs_hash(); ref->base = obj; insert_ref_hash(ref, refs_hash, refs_hash_size); nr_object_refs = nr; } struct object_refs *lookup_object_refs(struct object *obj) { struct object_refs *ref; int j; /* nothing to lookup */ if (!refs_hash_size) return NULL; j = hash_obj(obj, refs_hash_size); while ((ref = refs_hash[j]) != NULL) { if (ref->base == obj) break; j++; if (j >= refs_hash_size) j = 0; } return ref; } struct object_refs *alloc_object_refs(unsigned count) { struct object_refs *refs; size_t size = sizeof(*refs) + count*sizeof(struct object *); refs = xcalloc(1, size); refs->count = count; return refs; } static int compare_object_pointers(const void *a, const void *b) { const struct object * const *pa = a; const struct object * const *pb = b; if (*pa == *pb) return 0; else if (*pa < *pb) return -1; else return 1; } void set_object_refs(struct object *obj, struct object_refs *refs) { unsigned int i, j; /* Do not install empty list of references */ if (refs->count < 1) { free(refs); return; } /* Sort the list and filter out duplicates */ qsort(refs->ref, refs->count, sizeof(refs->ref[0]), compare_object_pointers); for (i = j = 1; i < refs->count; i++) { if (refs->ref[i] != refs->ref[i - 1]) refs->ref[j++] = refs->ref[i]; } if (j < refs->count) { /* Duplicates were found - reallocate list */ size_t size = sizeof(*refs) + j*sizeof(struct object *); refs->count = j; refs = xrealloc(refs, size); } for (i = 0; i < refs->count; i++) refs->ref[i]->used = 1; add_object_refs(obj, refs); } void mark_reachable(struct object *obj, unsigned int mask) { const struct object_refs *refs; if (!track_object_refs) die("cannot do reachability with object refs turned off"); /* If we've been here already, don't bother */ if (obj->flags & mask) return; obj->flags |= mask; refs = lookup_object_refs(obj); if (refs) { unsigned i; for (i = 0; i < refs->count; i++) mark_reachable(refs->ref[i], mask); } }