compat: Add simplified merge sort implementation from glibc
[gitweb.git] / commit.c
index b5092658724e2172abdf54a1af6d91bda0e176da..8b8fb04d1f6107da15cb142485d80cabe7a3828e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -8,22 +8,6 @@
 
 int save_commit_buffer = 1;
 
-struct sort_node
-{
-       /*
-        * the number of children of the associated commit
-        * that also occur in the list being sorted.
-        */
-       unsigned int indegree;
-
-       /*
-        * reference to original list item that we will re-use
-        * on output.
-        */
-       struct commit_list * list_item;
-
-};
-
 const char *commit_type = "commit";
 
 static struct commit *check_commit(struct object *obj,
@@ -64,19 +48,32 @@ struct commit *lookup_commit(const unsigned char *sha1)
        return check_commit(obj, sha1, 0);
 }
 
-static unsigned long parse_commit_date(const char *buf)
+static unsigned long parse_commit_date(const char *buf, const char *tail)
 {
        unsigned long date;
+       const char *dateptr;
 
+       if (buf + 6 >= tail)
+               return 0;
        if (memcmp(buf, "author", 6))
                return 0;
-       while (*buf++ != '\n')
+       while (buf < tail && *buf++ != '\n')
                /* nada */;
+       if (buf + 9 >= tail)
+               return 0;
        if (memcmp(buf, "committer", 9))
                return 0;
-       while (*buf++ != '>')
+       while (buf < tail && *buf++ != '>')
+               /* nada */;
+       if (buf >= tail)
+               return 0;
+       dateptr = buf;
+       while (buf < tail && *buf++ != '\n')
                /* nada */;
-       date = strtoul(buf, NULL, 10);
+       if (buf >= tail)
+               return 0;
+       /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
+       date = strtoul(dateptr, NULL, 10);
        if (date == ULONG_MAX)
                date = 0;
        return date;
@@ -252,9 +249,9 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
                return 0;
        item->object.parsed = 1;
        tail += size;
-       if (tail <= bufptr + 5 || memcmp(bufptr, "tree ", 5))
+       if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n')
                return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
-       if (tail <= bufptr + 45 || get_sha1_hex(bufptr + 5, parent) < 0)
+       if (get_sha1_hex(bufptr + 5, parent) < 0)
                return error("bad tree pointer in commit %s",
                             sha1_to_hex(item->object.sha1));
        item->tree = lookup_tree(parent);
@@ -291,7 +288,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
                        n_refs++;
                }
        }
-       item->date = parse_commit_date(bufptr);
+       item->date = parse_commit_date(bufptr, tail);
 
        if (track_object_refs) {
                unsigned i = 0;
@@ -431,69 +428,38 @@ struct commit *pop_commit(struct commit_list **stack)
        return item;
 }
 
-void topo_sort_default_setter(struct commit *c, void *data)
-{
-       c->util = data;
-}
-
-void *topo_sort_default_getter(struct commit *c)
-{
-       return c->util;
-}
-
 /*
  * Performs an in-place topological sort on the list supplied.
  */
 void sort_in_topological_order(struct commit_list ** list, int lifo)
 {
-       sort_in_topological_order_fn(list, lifo, topo_sort_default_setter,
-                                    topo_sort_default_getter);
-}
-
-void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
-                                 topo_sort_set_fn_t setter,
-                                 topo_sort_get_fn_t getter)
-{
-       struct commit_list * next = *list;
-       struct commit_list * work = NULL, **insert;
-       struct commit_list ** pptr = list;
-       struct sort_node * nodes;
-       struct sort_node * next_nodes;
-       int count = 0;
-
-       /* determine the size of the list */
-       while (next) {
-               next = next->next;
-               count++;
-       }
+       struct commit_list *next, *orig = *list;
+       struct commit_list *work, **insert;
+       struct commit_list **pptr;
 
-       if (!count)
+       if (!orig)
                return;
-       /* allocate an array to help sort the list */
-       nodes = xcalloc(count, sizeof(*nodes));
-       /* link the list to the array */
-       next_nodes = nodes;
-       next=*list;
-       while (next) {
-               next_nodes->list_item = next;
-               setter(next->item, next_nodes);
-               next_nodes++;
-               next = next->next;
+       *list = NULL;
+
+       /* Mark them and clear the indegree */
+       for (next = orig; next; next = next->next) {
+               struct commit *commit = next->item;
+               commit->object.flags |= TOPOSORT;
+               commit->indegree = 0;
        }
+
        /* update the indegree */
-       next=*list;
-       while (next) {
+       for (next = orig; next; next = next->next) {
                struct commit_list * parents = next->item->parents;
                while (parents) {
-                       struct commit * parent=parents->item;
-                       struct sort_node * pn = (struct sort_node *) getter(parent);
+                       struct commit *parent = parents->item;
 
-                       if (pn)
-                               pn->indegree++;
-                       parents=parents->next;
+                       if (parent->object.flags & TOPOSORT)
+                               parent->indegree++;
+                       parents = parents->next;
                }
-               next=next->next;
        }
+
        /*
         * find the tips
         *
@@ -501,55 +467,56 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
         *
         * the tips serve as a starting set for the work queue.
         */
-       next=*list;
+       work = NULL;
        insert = &work;
-       while (next) {
-               struct sort_node * node = (struct sort_node *) getter(next->item);
+       for (next = orig; next; next = next->next) {
+               struct commit *commit = next->item;
 
-               if (node->indegree == 0) {
-                       insert = &commit_list_insert(next->item, insert)->next;
-               }
-               next=next->next;
+               if (!commit->indegree)
+                       insert = &commit_list_insert(commit, insert)->next;
        }
 
        /* process the list in topological order */
        if (!lifo)
                sort_by_date(&work);
+
+       pptr = list;
+       *list = NULL;
        while (work) {
-               struct commit * work_item = pop_commit(&work);
-               struct sort_node * work_node = (struct sort_node *) getter(work_item);
-               struct commit_list * parents = work_item->parents;
+               struct commit *commit;
+               struct commit_list *parents, *work_item;
 
-               while (parents) {
-                       struct commit * parent=parents->item;
-                       struct sort_node * pn = (struct sort_node *) getter(parent);
-
-                       if (pn) {
-                               /*
-                                * parents are only enqueued for emission
-                                * when all their children have been emitted thereby
-                                * guaranteeing topological order.
-                                */
-                               pn->indegree--;
-                               if (!pn->indegree) {
-                                       if (!lifo)
-                                               insert_by_date(parent, &work);
-                                       else
-                                               commit_list_insert(parent, &work);
-                               }
+               work_item = work;
+               work = work_item->next;
+               work_item->next = NULL;
+
+               commit = work_item->item;
+               for (parents = commit->parents; parents ; parents = parents->next) {
+                       struct commit *parent=parents->item;
+
+                       if (!(parent->object.flags & TOPOSORT))
+                               continue;
+
+                       /*
+                        * parents are only enqueued for emission
+                        * when all their children have been emitted thereby
+                        * guaranteeing topological order.
+                        */
+                       if (!--parent->indegree) {
+                               if (!lifo)
+                                       insert_by_date(parent, &work);
+                               else
+                                       commit_list_insert(parent, &work);
                        }
-                       parents=parents->next;
                }
                /*
                 * work_item is a commit all of whose children
                 * have already been emitted. we can emit it now.
                 */
-               *pptr = work_node->list_item;
-               pptr = &(*pptr)->next;
-               *pptr = NULL;
-               setter(work_item, NULL);
+               commit->object.flags &= ~TOPOSORT;
+               *pptr = work_item;
+               pptr = &work_item->next;
        }
-       free(nodes);
 }
 
 /* merge-base stuff */