Commit e40f1c2d by Russell Belfer

Make tree iterator handle icase equivalence

There is a serious bug in the previous tree iterator implementation.
If case insensitivity resulted in member elements being equivalent
to one another, and those member elements were trees, then the
children of the colliding elements would be processed in sequence
instead of in a single flattened list.  This meant that the tree
iterator was not truly acting like a case-insensitive list.

This completely reworks the tree iterator to manage lists with
case insensitive equivalence classes and advance through the items
in a unified manner in a single sorted frame.

It is possible that at a future date we might want to update this
to separate the case insensitive and case sensitive tree iterators
so that the case sensitive one could be a minimal amount of code
and the insensitive one would always know what it needed to do
without checking flags.

But there would be so much shared code between the two, that I'm
not sure it that's a win.  For now, this gets what we need.

More tests are needed, though.
parent 9bea03ce
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include "hashsig.h" #include "hashsig.h"
#include "fileops.h" #include "fileops.h"
#include "util.h"
typedef uint32_t hashsig_t; typedef uint32_t hashsig_t;
typedef uint64_t hashsig_state; typedef uint64_t hashsig_state;
...@@ -19,7 +20,7 @@ typedef uint64_t hashsig_state; ...@@ -19,7 +20,7 @@ typedef uint64_t hashsig_state;
#define HASHSIG_HEAP_SIZE ((1 << 7) - 1) #define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
typedef int (GIT_STDLIB_CALL *hashsig_cmp)(const void *a, const void *b); typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
typedef struct { typedef struct {
int size, asize; int size, asize;
...@@ -53,15 +54,17 @@ static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp) ...@@ -53,15 +54,17 @@ static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
h->cmp = cmp; h->cmp = cmp;
} }
static int GIT_STDLIB_CALL hashsig_cmp_max(const void *a, const void *b) static int hashsig_cmp_max(const void *a, const void *b, void *payload)
{ {
hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b; hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
GIT_UNUSED(payload);
return (av < bv) ? -1 : (av > bv) ? 1 : 0; return (av < bv) ? -1 : (av > bv) ? 1 : 0;
} }
static int GIT_STDLIB_CALL hashsig_cmp_min(const void *a, const void *b) static int hashsig_cmp_min(const void *a, const void *b, void *payload)
{ {
hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b; hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
GIT_UNUSED(payload);
return (av > bv) ? -1 : (av < bv) ? 1 : 0; return (av > bv) ? -1 : (av < bv) ? 1 : 0;
} }
...@@ -69,7 +72,7 @@ static void hashsig_heap_up(hashsig_heap *h, int el) ...@@ -69,7 +72,7 @@ static void hashsig_heap_up(hashsig_heap *h, int el)
{ {
int parent_el = HEAP_PARENT_OF(el); int parent_el = HEAP_PARENT_OF(el);
while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el]) > 0) { while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el], NULL) > 0) {
hashsig_t t = h->values[el]; hashsig_t t = h->values[el];
h->values[el] = h->values[parent_el]; h->values[el] = h->values[parent_el];
h->values[parent_el] = t; h->values[parent_el] = t;
...@@ -92,10 +95,10 @@ static void hashsig_heap_down(hashsig_heap *h, int el) ...@@ -92,10 +95,10 @@ static void hashsig_heap_down(hashsig_heap *h, int el)
lv = h->values[lel]; lv = h->values[lel];
rv = h->values[rel]; rv = h->values[rel];
if (h->cmp(&v, &lv) < 0 && h->cmp(&v, &rv) < 0) if (h->cmp(&v, &lv, NULL) < 0 && h->cmp(&v, &rv, NULL) < 0)
break; break;
swapel = (h->cmp(&lv, &rv) < 0) ? lel : rel; swapel = (h->cmp(&lv, &rv, NULL) < 0) ? lel : rel;
h->values[el] = h->values[swapel]; h->values[el] = h->values[swapel];
h->values[swapel] = v; h->values[swapel] = v;
...@@ -107,13 +110,13 @@ static void hashsig_heap_down(hashsig_heap *h, int el) ...@@ -107,13 +110,13 @@ static void hashsig_heap_down(hashsig_heap *h, int el)
static void hashsig_heap_sort(hashsig_heap *h) static void hashsig_heap_sort(hashsig_heap *h)
{ {
/* only need to do this at the end for signature comparison */ /* only need to do this at the end for signature comparison */
qsort(h->values, h->size, sizeof(hashsig_t), h->cmp); git__qsort_r(h->values, h->size, sizeof(hashsig_t), h->cmp, NULL);
} }
static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val) static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
{ {
/* if heap is full, pop top if new element should replace it */ /* if heap is full, pop top if new element should replace it */
if (h->size == h->asize && h->cmp(&val, &h->values[0]) > 0) { if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
h->size--; h->size--;
h->values[0] = h->values[h->size]; h->values[0] = h->values[h->size];
hashsig_heap_down(h, 0); hashsig_heap_down(h, 0);
...@@ -343,7 +346,7 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b) ...@@ -343,7 +346,7 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
/* hash heaps are sorted - just look for overlap vs total */ /* hash heaps are sorted - just look for overlap vs total */
for (i = 0, j = 0; i < a->size && j < b->size; ) { for (i = 0, j = 0; i < a->size && j < b->size; ) {
cmp = a->cmp(&a->values[i], &b->values[j]); cmp = a->cmp(&a->values[i], &b->values[j], NULL);
if (cmp < 0) if (cmp < 0)
++i; ++i;
......
...@@ -32,14 +32,13 @@ typedef int git_file; ...@@ -32,14 +32,13 @@ typedef int git_file;
* Standard POSIX Methods * Standard POSIX Methods
* *
* All the methods starting with the `p_` prefix are * All the methods starting with the `p_` prefix are
* direct ports of the standard POSIX methods. * direct ports of the standard POSIX methods.
* *
* Some of the methods are slightly wrapped to provide * Some of the methods are slightly wrapped to provide
* saner defaults. Some of these methods are emulated * saner defaults. Some of these methods are emulated
* in Windows platforns. * in Windows platforns.
* *
* Use your manpages to check the docs on these. * Use your manpages to check the docs on these.
* Straightforward
*/ */
extern int p_read(git_file fd, void *buf, size_t cnt); extern int p_read(git_file fd, void *buf, size_t cnt);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define INCLUDE_posix__w32_h__ #define INCLUDE_posix__w32_h__
#include <stdio.h> #include <stdio.h>
#include <sys/param.h>
#define p_lstat(p,b) lstat(p,b) #define p_lstat(p,b) lstat(p,b)
#define p_readlink(a, b, c) readlink(a, b, c) #define p_readlink(a, b, c) readlink(a, b, c)
......
...@@ -607,3 +607,33 @@ size_t git__unescape(char *str) ...@@ -607,3 +607,33 @@ size_t git__unescape(char *str)
return (pos - str); return (pos - str);
} }
#if defined(GIT_WIN32) || defined(BSD)
typedef struct {
git__qsort_r_cmp cmp;
void *payload;
} git__qsort_r_glue;
static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
void *payload, const void *a, const void *b)
{
git__qsort_r_glue *glue = payload;
return glue->cmp(a, b, glue->payload);
}
#endif
void git__qsort_r(
void *els, size_t nel, size_t elsize, git__qsort_r_cmp cmp, void *payload)
{
#if defined(GIT_WIN32)
git__qsort_r_glue glue = { cmp, payload };
qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
#else
#if defined(BSD)
git__qsort_r_glue glue = { cmp, payload };
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
#else
qsort_r(els, nel, elsize, cmp, payload);
#endif
#endif
}
...@@ -151,6 +151,10 @@ typedef int (*git__tsort_r_cmp)(const void *a, const void *b, void *payload); ...@@ -151,6 +151,10 @@ typedef int (*git__tsort_r_cmp)(const void *a, const void *b, void *payload);
extern void git__tsort_r( extern void git__tsort_r(
void **dst, size_t size, git__tsort_r_cmp cmp, void *payload); void **dst, size_t size, git__tsort_r_cmp cmp, void *payload);
typedef int (*git__qsort_r_cmp)(const void *a, const void *b, void *payload);
extern void git__qsort_r(
void *els, size_t nel, size_t elsize, git__qsort_r_cmp cmp, void *payload);
/** /**
* @param position If non-NULL, this will be set to the position where the * @param position If non-NULL, this will be set to the position where the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment