Code

Merge branch 'jp/get-ref-dir-unsorted'
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Oct 2011 22:56:19 +0000 (15:56 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Oct 2011 22:56:19 +0000 (15:56 -0700)
* jp/get-ref-dir-unsorted:
  refs.c: free duplicate entries in the ref array instead of leaking them
  refs.c: abort ref search if ref array is empty
  refs.c: ensure struct whose member may be passed to realloc is initialized
  refs: Use binary search to lookup refs faster
  Don't sort ref_list too early

Conflicts:
refs.c

1  2 
refs.c
refs.h

diff --cc refs.c
index 8c69243b2a04224e75ca34bed404782fbb4b9885,df392976043c9ef025b3c55469810d768eddf794..9911c97b69a66ba0a4c7d3aff33b9d7b1d006796
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -53,101 -57,75 +57,77 @@@ static void add_ref(const char *name, c
  
        /* Allocate it and add it in.. */
        len = strlen(name) + 1;
-       entry = xmalloc(sizeof(struct ref_list) + len);
+       entry = xmalloc(sizeof(struct ref_entry) + len);
        hashcpy(entry->sha1, sha1);
        hashclr(entry->peeled);
 +      if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
 +              die("Reference has invalid format: '%s'", name);
        memcpy(entry->name, name, len);
        entry->flag = flag;
-       entry->next = list;
        if (new_entry)
                *new_entry = entry;
-       return entry;
+       ALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc);
+       refs->refs[refs->nr++] = entry;
  }
  
- /* merge sort the ref list */
- static struct ref_list *sort_ref_list(struct ref_list *list)
+ static int ref_entry_cmp(const void *a, const void *b)
  {
-       int psize, qsize, last_merge_count, cmp;
-       struct ref_list *p, *q, *l, *e;
-       struct ref_list *new_list = list;
-       int k = 1;
-       int merge_count = 0;
+       struct ref_entry *one = *(struct ref_entry **)a;
+       struct ref_entry *two = *(struct ref_entry **)b;
+       return strcmp(one->name, two->name);
+ }
  
-       if (!list)
-               return list;
+ static void sort_ref_array(struct ref_array *array)
+ {
+       int i = 0, j = 1;
  
-       do {
-               last_merge_count = merge_count;
-               merge_count = 0;
+       /* Nothing to sort unless there are at least two entries */
+       if (array->nr < 2)
+               return;
  
-               psize = 0;
+       qsort(array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);
  
-               p = new_list;
-               q = new_list;
-               new_list = NULL;
-               l = NULL;
+       /* Remove any duplicates from the ref_array */
+       for (; j < array->nr; j++) {
+               struct ref_entry *a = array->refs[i];
+               struct ref_entry *b = array->refs[j];
+               if (!strcmp(a->name, b->name)) {
+                       if (hashcmp(a->sha1, b->sha1))
+                               die("Duplicated ref, and SHA1s don't match: %s",
+                                   a->name);
+                       warning("Duplicated ref: %s", a->name);
+                       free(b);
+                       continue;
+               }
+               i++;
+               array->refs[i] = array->refs[j];
+       }
+       array->nr = i + 1;
+ }
  
-               while (p) {
-                       merge_count++;
+ static struct ref_entry *search_ref_array(struct ref_array *array, const char *name)
+ {
+       struct ref_entry *e, **r;
+       int len;
  
-                       while (psize < k && q->next) {
-                               q = q->next;
-                               psize++;
-                       }
-                       qsize = k;
-                       while ((psize > 0) || (qsize > 0 && q)) {
-                               if (qsize == 0 || !q) {
-                                       e = p;
-                                       p = p->next;
-                                       psize--;
-                               } else if (psize == 0) {
-                                       e = q;
-                                       q = q->next;
-                                       qsize--;
-                               } else {
-                                       cmp = strcmp(q->name, p->name);
-                                       if (cmp < 0) {
-                                               e = q;
-                                               q = q->next;
-                                               qsize--;
-                                       } else if (cmp > 0) {
-                                               e = p;
-                                               p = p->next;
-                                               psize--;
-                                       } else {
-                                               if (hashcmp(q->sha1, p->sha1))
-                                                       die("Duplicated ref, and SHA1s don't match: %s",
-                                                           q->name);
-                                               warning("Duplicated ref: %s", q->name);
-                                               e = q;
-                                               q = q->next;
-                                               qsize--;
-                                               free(e);
-                                               e = p;
-                                               p = p->next;
-                                               psize--;
-                                       }
-                               }
+       if (name == NULL)
+               return NULL;
  
-                               e->next = NULL;
+       if (!array->nr)
+               return NULL;
  
-                               if (l)
-                                       l->next = e;
-                               if (!new_list)
-                                       new_list = e;
-                               l = e;
-                       }
+       len = strlen(name) + 1;
+       e = xmalloc(sizeof(struct ref_entry) + len);
+       memcpy(e->name, name, len);
+       r = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);
  
-                       p = q;
-               };
+       free(e);
  
-               k = k * 2;
-       } while ((last_merge_count != merge_count) || (last_merge_count != 1));
+       if (r == NULL)
+               return NULL;
  
-       return new_list;
+       return *r;
  }
  
  /*
   * when doing a full libification.
   */
  static struct cached_refs {
 +      struct cached_refs *next;
        char did_loose;
        char did_packed;
-       struct ref_list *loose;
-       struct ref_list *packed;
+       struct ref_array loose;
+       struct ref_array packed;
 -} cached_refs, submodule_refs;
 +      /* The submodule name, or "" for the main repo. */
 +      char name[FLEX_ARRAY];
 +} *cached_refs;
 +
- static struct ref_list *current_ref;
+ static struct ref_entry *current_ref;
  
- static struct ref_list *extra_refs;
+ static struct ref_array extra_refs;
  
- static void free_ref_list(struct ref_list *list)
+ static void free_ref_array(struct ref_array *array)
  {
-       struct ref_list *next;
-       for ( ; list; list = next) {
-               next = list->next;
-               free(list);
-       }
+       int i;
+       for (i = 0; i < array->nr; i++)
+               free(array->refs[i]);
+       free(array->refs);
+       array->nr = array->alloc = 0;
+       array->refs = NULL;
  }
  
 -static void invalidate_cached_refs(void)
 +static void clear_cached_refs(struct cached_refs *ca)
  {
-       if (ca->did_loose && ca->loose)
-               free_ref_list(ca->loose);
-       if (ca->did_packed && ca->packed)
-               free_ref_list(ca->packed);
-       ca->loose = ca->packed = NULL;
 -      struct cached_refs *ca = &cached_refs;
 -
+       if (ca->did_loose)
+               free_ref_array(&ca->loose);
+       if (ca->did_packed)
+               free_ref_array(&ca->packed);
        ca->did_loose = ca->did_packed = 0;
  }
  
 -static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
 +static struct cached_refs *create_cached_refs(const char *submodule)
 +{
 +      int len;
 +      struct cached_refs *refs;
 +      if (!submodule)
 +              submodule = "";
 +      len = strlen(submodule) + 1;
-       refs = xmalloc(sizeof(struct cached_refs) + len);
-       refs->next = NULL;
-       refs->did_loose = refs->did_packed = 0;
-       refs->loose = refs->packed = NULL;
++      refs = xcalloc(1, sizeof(struct cached_refs) + len);
 +      memcpy(refs->name, submodule, len);
 +      return refs;
 +}
 +
 +/*
 + * Return a pointer to a cached_refs for the specified submodule. For
 + * the main repository, use submodule==NULL. The returned structure
 + * will be allocated and initialized but not necessarily populated; it
 + * should not be freed.
 + */
 +static struct cached_refs *get_cached_refs(const char *submodule)
 +{
 +      struct cached_refs *refs = cached_refs;
 +      if (!submodule)
 +              submodule = "";
 +      while (refs) {
 +              if (!strcmp(submodule, refs->name))
 +                      return refs;
 +              refs = refs->next;
 +      }
 +
 +      refs = create_cached_refs(submodule);
 +      refs->next = cached_refs;
 +      cached_refs = refs;
 +      return refs;
 +}
 +
 +static void invalidate_cached_refs(void)
 +{
 +      struct cached_refs *refs = cached_refs;
 +      while (refs) {
 +              clear_cached_refs(refs);
 +              refs = refs->next;
 +      }
 +}
 +
- static struct ref_list *read_packed_refs(FILE *f)
++static void read_packed_refs(FILE *f, struct ref_array *array)
  {
-       struct ref_list *list = NULL;
-       struct ref_list *last = NULL;
+       struct ref_entry *last = NULL;
        char refline[PATH_MAX];
        int flag = REF_ISPACKED;
  
  
                name = parse_ref_line(refline, sha1);
                if (name) {
-                       list = add_ref(name, sha1, flag, list, &last);
 -                      add_ref(name, sha1, flag, &cached_refs->packed, &last);
++                      add_ref(name, sha1, flag, array, &last);
                        continue;
                }
                if (last &&
                    !get_sha1_hex(refline + 1, sha1))
                        hashcpy(last->peeled, sha1);
        }
-       return sort_ref_list(list);
 -      sort_ref_array(&cached_refs->packed);
++      sort_ref_array(array);
  }
  
  void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
  
  void clear_extra_refs(void)
  {
-       free_ref_list(extra_refs);
-       extra_refs = NULL;
+       free_ref_array(&extra_refs);
  }
  
- static struct ref_list *get_packed_refs(const char *submodule)
+ static struct ref_array *get_packed_refs(const char *submodule)
  {
 -      const char *packed_refs_file;
 -      struct cached_refs *refs;
 +      struct cached_refs *refs = get_cached_refs(submodule);
  
 -      if (submodule) {
 -              packed_refs_file = git_path_submodule(submodule, "packed-refs");
 -              refs = &submodule_refs;
 -              free_ref_array(&refs->packed);
 -      } else {
 -              packed_refs_file = git_path("packed-refs");
 -              refs = &cached_refs;
 -      }
 +      if (!refs->did_packed) {
 +              const char *packed_refs_file;
 +              FILE *f;
  
 -      if (!refs->did_packed || submodule) {
 -              FILE *f = fopen(packed_refs_file, "r");
 +              if (submodule)
 +                      packed_refs_file = git_path_submodule(submodule, "packed-refs");
 +              else
 +                      packed_refs_file = git_path("packed-refs");
 +              f = fopen(packed_refs_file, "r");
-               refs->packed = NULL;
                if (f) {
-                       refs->packed = read_packed_refs(f);
 -                      read_packed_refs(f, refs);
++                      read_packed_refs(f, &refs->packed);
                        fclose(f);
                }
                refs->did_packed = 1;
@@@ -404,15 -332,21 +377,16 @@@ void warn_dangling_symref(FILE *fp, con
        for_each_rawref(warn_if_dangling_symref, &data);
  }
  
- static struct ref_list *get_loose_refs(const char *submodule)
+ static struct ref_array *get_loose_refs(const char *submodule)
  {
 -      if (submodule) {
 -              free_ref_array(&submodule_refs.loose);
 -              get_ref_dir(submodule, "refs", &submodule_refs.loose);
 -              sort_ref_array(&submodule_refs.loose);
 -              return &submodule_refs.loose;
 -      }
 +      struct cached_refs *refs = get_cached_refs(submodule);
  
 -      if (!cached_refs.did_loose) {
 -              get_ref_dir(NULL, "refs", &cached_refs.loose);
 -              sort_ref_array(&cached_refs.loose);
 -              cached_refs.did_loose = 1;
 +      if (!refs->did_loose) {
-               refs->loose = get_ref_dir(submodule, "refs", NULL);
++              get_ref_dir(submodule, "refs", &refs->loose);
++              sort_ref_array(&refs->loose);
 +              refs->did_loose = 1;
        }
-       return refs->loose;
 -      return &cached_refs.loose;
++      return &refs->loose;
  }
  
  /* We allow "recursive" symbolic refs. Only within reason, though */
  
  static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)
  {
--      FILE *f;
-       struct ref_list *packed_refs;
-       struct ref_list *ref;
-       int retval;
 -      struct cached_refs refs;
 -      struct ref_entry *ref;
+       int retval = -1;
++      struct ref_entry *ref;
++      struct ref_array *array = get_packed_refs(name);
  
--      strcpy(name + pathlen, "packed-refs");
--      f = fopen(name, "r");
--      if (!f)
--              return -1;
-       packed_refs = read_packed_refs(f);
 -      memset(&refs, 0, sizeof(refs));
 -      read_packed_refs(f, &refs);
--      fclose(f);
-       ref = packed_refs;
-       retval = -1;
-       while (ref) {
-               if (!strcmp(ref->name, refname)) {
-                       retval = 0;
-                       memcpy(result, ref->sha1, 20);
-                       break;
-               }
-               ref = ref->next;
 -      ref = search_ref_array(&refs.packed, refname);
++      ref = search_ref_array(array, refname);
+       if (ref != NULL) {
+               memcpy(result, ref->sha1, 20);
+               retval = 0;
        }
-       free_ref_list(packed_refs);
 -      free_ref_array(&refs.packed);
        return retval;
  }
  
@@@ -510,22 -440,17 +471,20 @@@ int resolve_gitlink_ref(const char *pat
  }
  
  /*
 - * If the "reading" argument is set, this function finds out what _object_
 - * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
 - * has to exist, and if it is symbolic, it has to point at an existing ref,
 - * because the "read" goes through the symref to the ref it points at.
 - *
 - * The access that is not "reading" may often be "writing", but does not
 - * have to; it can be merely checking _where it leads to_. If it is a
 - * prelude to "writing" to the ref, a write to a symref that points at
 - * yet-to-be-born ref will create the real ref pointed by the symref.
 - * reading=0 allows the caller to check where such a symref leads to.
 + * Try to read ref from the packed references.  On success, set sha1
 + * and return 0; otherwise, return -1.
   */
-       struct ref_list *list = get_packed_refs(NULL);
-       while (list) {
-               if (!strcmp(ref, list->name)) {
-                       hashcpy(sha1, list->sha1);
-                       return 0;
-               }
-               list = list->next;
 +static int get_packed_ref(const char *ref, unsigned char *sha1)
 +{
++      struct ref_array *packed = get_packed_refs(NULL);
++      struct ref_entry *entry = search_ref_array(packed, ref);
++      if (entry) {
++              hashcpy(sha1, entry->sha1);
++              return 0;
 +      }
 +      return -1;
 +}
 +
  const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
  {
        int depth = MAXDEPTH;
@@@ -649,9 -554,9 +608,9 @@@ int read_ref(const char *ref, unsigned 
  
  #define DO_FOR_EACH_INCLUDE_BROKEN 01
  static int do_one_ref(const char *base, each_ref_fn fn, int trim,
-                     int flags, void *cb_data, struct ref_list *entry)
+                     int flags, void *cb_data, struct ref_entry *entry)
  {
 -      if (strncmp(base, entry->name, trim))
 +      if (prefixcmp(entry->name, base))
                return 0;
  
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
@@@ -1931,12 -1792,6 +1884,12 @@@ int update_ref(const char *action, cons
        return 0;
  }
  
- int ref_exists(char *refname)
++int ref_exists(const char *refname)
 +{
 +      unsigned char sha1[20];
 +      return !!resolve_ref(refname, sha1, 1, NULL);
 +}
 +
  struct ref *find_ref_by_name(const struct ref *list, const char *name)
  {
        for ( ; list; list = list->next)
diff --cc refs.h
index d5ac133336dc0da45cd916207d12a5e0e4237ae3,5e7a9a59f5f6b687d15eb37a11696433bdc8f15c..0229c57132f53b85e4d6af8b62627383a49527ac
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -57,7 -54,6 +57,7 @@@ extern void warn_dangling_symref(FILE *
   */
  extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags);
  extern void clear_extra_refs(void);
- extern int ref_exists(char *);
++extern int ref_exists(const char *);
  
  extern int peel_ref(const char *, unsigned char *);