packaging: Add contrib installation
[platform/upstream/git.git] / alloc.c
diff --git a/alloc.c b/alloc.c
index eb22a45..957a0af 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -4,8 +4,7 @@
  * Copyright (C) 2006 Linus Torvalds
  *
  * The standard malloc/free wastes too much space for objects, partly because
- * it maintains all the allocation infrastructure (which isn't needed, since
- * we never free an object descriptor anyway), but even more because it ends
+ * it maintains all the allocation infrastructure, but even more because it ends
  * up with maximal alignment because it doesn't know what the object alignment
  * for the new allocation is.
  */
 #include "tree.h"
 #include "commit.h"
 #include "tag.h"
+#include "alloc.h"
 
 #define BLOCKING 1024
 
-#define DEFINE_ALLOCATOR(name, type)                           \
-static unsigned int name##_allocs;                             \
-void *alloc_##name##_node(void)                                        \
-{                                                              \
-       static int nr;                                          \
-       static type *block;                                     \
-       void *ret;                                              \
-                                                               \
-       if (!nr) {                                              \
-               nr = BLOCKING;                                  \
-               block = xmalloc(BLOCKING * sizeof(type));       \
-       }                                                       \
-       nr--;                                                   \
-       name##_allocs++;                                        \
-       ret = block++;                                          \
-       memset(ret, 0, sizeof(type));                           \
-       return ret;                                             \
-}
-
 union any_object {
        struct object object;
        struct blob blob;
@@ -45,17 +26,100 @@ union any_object {
        struct tag tag;
 };
 
-DEFINE_ALLOCATOR(blob, struct blob)
-DEFINE_ALLOCATOR(tree, struct tree)
-DEFINE_ALLOCATOR(raw_commit, struct commit)
-DEFINE_ALLOCATOR(tag, struct tag)
-DEFINE_ALLOCATOR(object, union any_object)
+struct alloc_state {
+       int count; /* total number of nodes allocated */
+       int nr;    /* number of nodes left in current allocation */
+       void *p;   /* first free node in current allocation */
+
+       /* bookkeeping of allocations */
+       void **slabs;
+       int slab_nr, slab_alloc;
+};
+
+struct alloc_state *allocate_alloc_state(void)
+{
+       return xcalloc(1, sizeof(struct alloc_state));
+}
+
+void clear_alloc_state(struct alloc_state *s)
+{
+       while (s->slab_nr > 0) {
+               s->slab_nr--;
+               free(s->slabs[s->slab_nr]);
+       }
+
+       FREE_AND_NULL(s->slabs);
+}
+
+static inline void *alloc_node(struct alloc_state *s, size_t node_size)
+{
+       void *ret;
+
+       if (!s->nr) {
+               s->nr = BLOCKING;
+               s->p = xmalloc(BLOCKING * node_size);
+
+               ALLOC_GROW(s->slabs, s->slab_nr + 1, s->slab_alloc);
+               s->slabs[s->slab_nr++] = s->p;
+       }
+       s->nr--;
+       s->count++;
+       ret = s->p;
+       s->p = (char *)s->p + node_size;
+       memset(ret, 0, node_size);
+
+       return ret;
+}
+
+void *alloc_blob_node(struct repository *r)
+{
+       struct blob *b = alloc_node(r->parsed_objects->blob_state, sizeof(struct blob));
+       b->object.type = OBJ_BLOB;
+       return b;
+}
+
+void *alloc_tree_node(struct repository *r)
+{
+       struct tree *t = alloc_node(r->parsed_objects->tree_state, sizeof(struct tree));
+       t->object.type = OBJ_TREE;
+       return t;
+}
+
+void *alloc_tag_node(struct repository *r)
+{
+       struct tag *t = alloc_node(r->parsed_objects->tag_state, sizeof(struct tag));
+       t->object.type = OBJ_TAG;
+       return t;
+}
+
+void *alloc_object_node(struct repository *r)
+{
+       struct object *obj = alloc_node(r->parsed_objects->object_state, sizeof(union any_object));
+       obj->type = OBJ_NONE;
+       return obj;
+}
+
+/*
+ * The returned count is to be used as an index into commit slabs,
+ * that are *NOT* maintained per repository, and that is why a single
+ * global counter is used.
+ */
+static unsigned int alloc_commit_index(void)
+{
+       static unsigned int parsed_commits_count;
+       return parsed_commits_count++;
+}
+
+void init_commit_node(struct commit *c)
+{
+       c->object.type = OBJ_COMMIT;
+       c->index = alloc_commit_index();
+}
 
-void *alloc_commit_node(void)
+void *alloc_commit_node(struct repository *r)
 {
-       static int commit_count;
-       struct commit *c = alloc_raw_commit_node();
-       c->index = commit_count++;
+       struct commit *c = alloc_node(r->parsed_objects->commit_state, sizeof(struct commit));
+       init_commit_node(c);
        return c;
 }
 
@@ -66,13 +130,14 @@ static void report(const char *name, unsigned int count, size_t size)
 }
 
 #define REPORT(name, type)     \
-    report(#name, name##_allocs, name##_allocs * sizeof(type) >> 10)
+    report(#name, r->parsed_objects->name##_state->count, \
+                 r->parsed_objects->name##_state->count * sizeof(type) >> 10)
 
-void alloc_report(void)
+void alloc_report(struct repository *r)
 {
        REPORT(blob, struct blob);
        REPORT(tree, struct tree);
-       REPORT(raw_commit, struct commit);
+       REPORT(commit, struct commit);
        REPORT(tag, struct tag);
        REPORT(object, union any_object);
 }