Commit 851ad65081793bb5fd65052907bf1c3c4e7e5729

Russell Belfer 2013-01-09T16:00:16

Add payload "_r" versions of bsearch and tsort git__bsearch and git__tsort did not pass a payload through to the comparison function. This makes it impossible to implement sorted lists where the sort order depends on external data (e.g. building a secondary sort order for the entries in a tree). This commit adds git__bsearch_r and git__tsort_r versions that pass a third parameter to the cmp function of a user payload.

diff --git a/src/tsort.c b/src/tsort.c
index 634fe26..97473be 100644
--- a/src/tsort.c
+++ b/src/tsort.c
@@ -23,9 +23,8 @@
 #	define MIN(x,y) (((x) < (y) ? (x) : (y)))
 #endif
 
-typedef int (*cmp_ptr_t)(const void *, const void *);
-
-static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
+static int binsearch(
+	void **dst, const void *x, size_t size, git__tsort_r_cmp cmp, void *payload)
 {
 	int l, c, r;
 	void *lx, *cx;
@@ -38,12 +37,12 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
 	lx = dst[l];
 
 	/* check for beginning conditions */
-	if (cmp(x, lx) < 0)
+	if (cmp(x, lx, payload) < 0)
 		return 0;
 
-	else if (cmp(x, lx) == 0) {
+	else if (cmp(x, lx, payload) == 0) {
 		int i = 1;
-		while (cmp(x, dst[i]) == 0)
+		while (cmp(x, dst[i], payload) == 0)
 			i++;
 		return i;
 	}
@@ -51,7 +50,7 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
 	/* guaranteed not to be >= rx */
 	cx = dst[c];
 	while (1) {
-		const int val = cmp(x, cx);
+		const int val = cmp(x, cx, payload);
 		if (val < 0) {
 			if (c - l <= 1) return c;
 			r = c;
@@ -62,7 +61,7 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
 		} else {
 			do {
 				cx = dst[++c];
-			} while (cmp(x, cx) == 0);
+			} while (cmp(x, cx, payload) == 0);
 			return c;
 		}
 		c = l + ((r - l) >> 1);
@@ -71,7 +70,8 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
 }
 
 /* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
-static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
+static void bisort(
+	void **dst, size_t start, size_t size, git__tsort_r_cmp cmp, void *payload)
 {
 	size_t i;
 	void *x;
@@ -80,12 +80,12 @@ static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
 	for (i = start; i < size; i++) {
 		int j;
 		/* If this entry is already correct, just move along */
-		if (cmp(dst[i - 1], dst[i]) <= 0)
+		if (cmp(dst[i - 1], dst[i], payload) <= 0)
 			continue;
 
 		/* Else we need to find the right place, shift everything over, and squeeze in */
 		x = dst[i];
-		location = binsearch(dst, x, i, cmp);
+		location = binsearch(dst, x, i, cmp, payload);
 		for (j = (int)i - 1; j >= location; j--) {
 			dst[j + 1] = dst[j];
 		}
@@ -102,7 +102,8 @@ struct tsort_run {
 
 struct tsort_store {
 	size_t alloc;
-	cmp_ptr_t cmp;
+	git__tsort_r_cmp cmp;
+	void *payload;
 	void **storage;
 };
 
@@ -118,7 +119,8 @@ static void reverse_elements(void **dst, ssize_t start, ssize_t end)
 	}
 }
 
-static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
+static ssize_t count_run(
+	void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
 {
 	ssize_t curr = start + 2;
 
@@ -126,7 +128,7 @@ static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_s
 		return 1;
 
 	if (start >= size - 2) {
-		if (store->cmp(dst[size - 2], dst[size - 1]) > 0) {
+		if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) {
 			void *tmp = dst[size - 1];
 			dst[size - 1] = dst[size - 2];
 			dst[size - 2] = tmp;
@@ -135,13 +137,15 @@ static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_s
 		return 2;
 	}
 
-	if (store->cmp(dst[start], dst[start + 1]) <= 0) {
-		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0)
+	if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) {
+		while (curr < size - 1 &&
+				store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0)
 			curr++;
 
 		return curr - start;
 	} else {
-		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0)
+		while (curr < size - 1 &&
+				store->cmp(dst[curr - 1], dst[curr], store->payload) > 0)
 			curr++;
 
 		/* reverse in-place */
@@ -219,7 +223,7 @@ static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr,
 
 		for (k = curr; k < curr + A + B; k++) {
 			if ((i < A) && (j < curr + A + B)) {
-				if (store->cmp(storage[i], dst[j]) <= 0)
+				if (store->cmp(storage[i], dst[j], store->payload) <= 0)
 					dst[k] = storage[i++];
 				else
 					dst[k] = dst[j++];
@@ -235,7 +239,7 @@ static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr,
 
 		for (k = curr + A + B - 1; k >= curr; k--) {
 			if ((i >= 0) && (j >= curr)) {
-				if (store->cmp(dst[j], storage[i]) > 0)
+				if (store->cmp(dst[j], storage[i], store->payload) > 0)
 					dst[k] = dst[j--];
 				else
 					dst[k] = storage[i--];
@@ -307,7 +311,7 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr,
 	if (run < minrun) run = minrun;\
 	if (run > (ssize_t)size - curr) run = size - curr;\
 	if (run > len) {\
-		bisort(&dst[curr], len, run, cmp);\
+		bisort(&dst[curr], len, run, cmp, payload);\
 		len = run;\
 	}\
 	run_stack[stack_curr].start = curr;\
@@ -329,7 +333,8 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr,
 }\
 while (0)
 
-void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
+void git__tsort_r(
+	void **dst, size_t size, git__tsort_r_cmp cmp, void *payload)
 {
 	struct tsort_store _store, *store = &_store;
 	struct tsort_run run_stack[128];
@@ -340,7 +345,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
 	ssize_t minrun;
 
 	if (size < 64) {
-		bisort(dst, 1, size, cmp);
+		bisort(dst, 1, size, cmp, payload);
 		return;
 	}
 
@@ -351,6 +356,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
 	store->alloc = 0;
 	store->storage = NULL;
 	store->cmp = cmp;
+	store->payload = payload;
 
 	PUSH_NEXT();
 	PUSH_NEXT();
@@ -365,3 +371,13 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
 		PUSH_NEXT();
 	}
 }
+
+static int tsort_r_cmp(const void *a, const void *b, void *payload)
+{
+	return ((git__tsort_cmp)payload)(a, b);
+}
+
+void git__tsort(void **dst, size_t size, git__tsort_cmp cmp)
+{
+	git__tsort_r(dst, size, tsort_r_cmp, cmp);
+}
diff --git a/src/util.c b/src/util.c
index 51173fa..30c4dc6 100644
--- a/src/util.c
+++ b/src/util.c
@@ -462,7 +462,7 @@ uint32_t git__hash(const void *key, int len, uint32_t seed)
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- */ 
+ */
 int git__bsearch(
 	void **array,
 	size_t array_len,
@@ -493,6 +493,37 @@ int git__bsearch(
 	return (cmp == 0) ? 0 : -1;
 }
 
+int git__bsearch_r(
+	void **array,
+	size_t array_len,
+	const void *key,
+	int (*compare_r)(const void *, const void *, void *),
+	void *payload,
+	size_t *position)
+{
+	unsigned int lim;
+	int cmp = -1;
+	void **part, **base = array;
+
+	for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {
+		part = base + (lim >> 1);
+		cmp = (*compare_r)(key, *part, payload);
+		if (cmp == 0) {
+			base = part;
+			break;
+		}
+		if (cmp > 0) { /* key > p; take right partition */
+			base = part + 1;
+			lim--;
+		} /* else take left partition */
+	}
+
+	if (position)
+		*position = (base - array);
+
+	return (cmp == 0) ? 0 : -1;
+}
+
 /**
  * A strcmp wrapper
  *
diff --git a/src/util.h b/src/util.h
index ee0d0e3..9bcd320 100644
--- a/src/util.h
+++ b/src/util.h
@@ -119,7 +119,15 @@ GIT_INLINE(const char *) git__next_line(const char *s)
 	return s;
 }
 
-extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));
+typedef int (*git__tsort_cmp)(const void *a, const void *b);
+
+extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);
+
+typedef int (*git__tsort_r_cmp)(const void *a, const void *b, void *payload);
+
+extern void git__tsort_r(
+	void **dst, size_t size, git__tsort_r_cmp cmp, void *payload);
+
 
 /**
  * @param position If non-NULL, this will be set to the position where the
@@ -130,7 +138,15 @@ extern int git__bsearch(
 	void **array,
 	size_t array_len,
 	const void *key,
-	int (*compare)(const void *, const void *),
+	int (*compare)(const void *key, const void *element),
+	size_t *position);
+
+extern int git__bsearch_r(
+	void **array,
+	size_t array_len,
+	const void *key,
+	int (*compare_r)(const void *key, const void *element, void *payload),
+	void *payload,
 	size_t *position);
 
 extern int git__strcmp_cb(const void *a, const void *b);