From 40ad937d47d6649a022f12f463ee39e83b1d9663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 22 May 2012 20:50:39 +0200 Subject: [PATCH 1/4] refs: convert parameter of search_ref_dir() to length-limited string Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- refs.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/refs.c b/refs.c index d6bdb47ad..c02833386 100644 --- a/refs.c +++ b/refs.c @@ -319,19 +319,19 @@ static void sort_ref_dir(struct ref_dir *dir); * (non-recursively), sorting dir if necessary. Return NULL if no * such entry is found. dir must already be complete. */ -static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname) +static struct ref_entry *search_ref_dir(struct ref_dir *dir, + const char *refname, size_t len) { struct ref_entry *e, **r; - int len; if (refname == NULL || !dir->nr) return NULL; sort_ref_dir(dir); - len = strlen(refname) + 1; - e = xmalloc(sizeof(struct ref_entry) + len); + e = xmalloc(sizeof(struct ref_entry) + len + 1); memcpy(e->name, refname, len); + e->name[len] = '\0'; r = bsearch(&e, dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp); @@ -353,7 +353,8 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname static struct ref_dir *search_for_subdir(struct ref_dir *dir, const char *subdirname, int mkdir) { - struct ref_entry *entry = search_ref_dir(dir, subdirname); + size_t len = strlen(subdirname); + struct ref_entry *entry = search_ref_dir(dir, subdirname, len); if (!entry) { if (!mkdir) return NULL; @@ -412,7 +413,7 @@ static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname) dir = find_containing_dir(dir, refname, 0); if (!dir) return NULL; - entry = search_ref_dir(dir, refname); + entry = search_ref_dir(dir, refname, strlen(refname)); return (entry && !(entry->flag & REF_DIR)) ? entry : NULL; } From b9146f517a312fed42d12b32e3ee3bd99944121d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 22 May 2012 20:50:52 +0200 Subject: [PATCH 2/4] refs: convert parameter of create_dir_entry() to length-limited string Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- refs.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/refs.c b/refs.c index c02833386..c5e167bb9 100644 --- a/refs.c +++ b/refs.c @@ -294,12 +294,13 @@ static void clear_ref_dir(struct ref_dir *dir) * "refs/heads/") or "" for the top-level directory. */ static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache, - const char *dirname, int incomplete) + const char *dirname, size_t len, + int incomplete) { struct ref_entry *direntry; - int len = strlen(dirname); direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1); - memcpy(direntry->name, dirname, len + 1); + memcpy(direntry->name, dirname, len); + direntry->name[len] = '\0'; direntry->u.subdir.ref_cache = ref_cache; direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0); return direntry; @@ -364,7 +365,7 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir, * therefore, create an empty record for it but mark * the record complete. */ - entry = create_dir_entry(dir->ref_cache, subdirname, 0); + entry = create_dir_entry(dir->ref_cache, subdirname, len, 0); add_entry_to_dir(dir, entry); } return get_ref_dir(entry); @@ -823,7 +824,7 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs) const char *packed_refs_file; FILE *f; - refs->packed = create_dir_entry(refs, "", 0); + refs->packed = create_dir_entry(refs, "", 0, 0); if (*refs->name) packed_refs_file = git_path_submodule(refs->name, "packed-refs"); else @@ -888,7 +889,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) } else if (S_ISDIR(st.st_mode)) { strbuf_addch(&refname, '/'); add_entry_to_dir(dir, - create_dir_entry(refs, refname.buf, 1)); + create_dir_entry(refs, refname.buf, + refname.len, 1)); } else { if (*refs->name) { hashclr(sha1); @@ -918,12 +920,12 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs) * are about to read the only subdirectory that can * hold references: */ - refs->loose = create_dir_entry(refs, "", 0); + refs->loose = create_dir_entry(refs, "", 0, 0); /* * Create an incomplete entry for "refs/": */ add_entry_to_dir(get_ref_dir(refs->loose), - create_dir_entry(refs, "refs/", 1)); + create_dir_entry(refs, "refs/", 5, 1)); } return get_ref_dir(refs->loose); } From dd02e72852280d73bfd845703476b230475b2776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 22 May 2012 20:50:58 +0200 Subject: [PATCH 3/4] refs: use strings directly in find_containing_dir() Convert the parameter subdirname of search_for_subdir() to a length-limted string and then simply pass the interesting slice of the refname from find_containing_dir(), thereby avoiding to duplicate the string. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- refs.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/refs.c b/refs.c index c5e167bb9..96e943c36 100644 --- a/refs.c +++ b/refs.c @@ -352,9 +352,9 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, * directory cannot be found. dir must already be complete. */ static struct ref_dir *search_for_subdir(struct ref_dir *dir, - const char *subdirname, int mkdir) + const char *subdirname, size_t len, + int mkdir) { - size_t len = strlen(subdirname); struct ref_entry *entry = search_ref_dir(dir, subdirname, len); if (!entry) { if (!mkdir) @@ -383,15 +383,11 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir, static struct ref_dir *find_containing_dir(struct ref_dir *dir, const char *refname, int mkdir) { - struct strbuf dirname; const char *slash; - strbuf_init(&dirname, PATH_MAX); for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { + size_t dirnamelen = slash - refname + 1; struct ref_dir *subdir; - strbuf_add(&dirname, - refname + dirname.len, - (slash + 1) - (refname + dirname.len)); - subdir = search_for_subdir(dir, dirname.buf, mkdir); + subdir = search_for_subdir(dir, refname, dirnamelen, mkdir); if (!subdir) { dir = NULL; break; @@ -399,7 +395,6 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir, dir = subdir; } - strbuf_release(&dirname); return dir; } From e1980c9d23c22ecfbcadbe91d304ba778b84b457 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 22 May 2012 14:03:29 -0700 Subject: [PATCH 4/4] refs: do not create ref_entry when searching The search_ref_dir() function is about looking up an existing ref_entry in a sorted array of ref_entry stored in dir->entries, but it still allocates a new ref_entry and frees it before returning. This is only because the call to bsearch(3) was coded in a suboptimal way. Unlike the comparison function given to qsort(3), the first parameter to its comparison function does not need to point at an object that is shaped like an element in the array. Introduce a new comparison function that takes a counted string as the key and an element in an array of ref_entry and give it to bsearch(), so that we do not have to allocate a new ref_entry that we will never return to the caller anyway. Signed-off-by: Junio C Hamano --- refs.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/refs.c b/refs.c index 96e943c36..52709ab7f 100644 --- a/refs.c +++ b/refs.c @@ -315,6 +315,23 @@ static int ref_entry_cmp(const void *a, const void *b) static void sort_ref_dir(struct ref_dir *dir); +struct string_slice { + size_t len; + const char *str; +}; + +static int ref_entry_cmp_sslice(const void *key_, const void *ent_) +{ + struct string_slice *key = (struct string_slice *)key_; + struct ref_entry *ent = *(struct ref_entry **)ent_; + int entlen = strlen(ent->name); + int cmplen = key->len < entlen ? key->len : entlen; + int cmp = memcmp(key->str, ent->name, cmplen); + if (cmp) + return cmp; + return key->len - entlen; +} + /* * Return the entry with the given refname from the ref_dir * (non-recursively), sorting dir if necessary. Return NULL if no @@ -323,20 +340,17 @@ static void sort_ref_dir(struct ref_dir *dir); static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname, size_t len) { - struct ref_entry *e, **r; + struct ref_entry **r; + struct string_slice key; if (refname == NULL || !dir->nr) return NULL; sort_ref_dir(dir); - - e = xmalloc(sizeof(struct ref_entry) + len + 1); - memcpy(e->name, refname, len); - e->name[len] = '\0'; - - r = bsearch(&e, dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp); - - free(e); + key.len = len; + key.str = refname; + r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries), + ref_entry_cmp_sslice); if (r == NULL) return NULL;