From f7b0798b5f6fd681c2bad12cd9857fc5df876e54 Mon Sep 17 00:00:00 2001 From: Rob Davies Date: Fri, 3 Jul 2026 16:36:38 +0100 Subject: [PATCH] Make reglist_sort_() stable Add a tie-breaker comparison so that reglist_sort_() preserves the original ordering when regions compare exactly. This only matters where the region list includes payload data for the regions being sorted, otherwise region entries that tie are indistinguishable. As in that case an array of pointers into the original region list is sorted, the pointer values can be used to break any ties. This resolves a test failure for the `bcftools csq --greedy` option on alpine Linux because its qsort() implementation did not give the same ordering as the glibc one. As glibc often (but not always!) uses mergesort for its qsort() implementation, it may have given the impression of being stable when in fact this is not guaranteed. Signed-off-by: Rob Davies --- regidx.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/regidx.c b/regidx.c index d0390efa..68d4d88d 100644 --- a/regidx.c +++ b/regidx.c @@ -143,7 +143,14 @@ static int cmp_reg_ptrs(const void *a, const void *b) } static int cmp_reg_ptrs2(const void *a, const void *b) { - return cmp_regs(*((reg_t**)a),*((reg_t**)b)); + reg_t **ra = (reg_t**) a; + reg_t **rb = (reg_t**) b; + int res = cmp_regs(*ra, *rb); + // Ensure sort is stable by comparing pointers. This is safe as we're + // sorting a list of pointers to the original regions rather than the + // regions themselves. As they all point into the same array, they + // can be safely compared and also preserve the original ordering. + return res ? res : (*ra > *rb) - (*ra < *rb); } inline int regidx_push(regidx_t *idx, char *chr_beg, char *chr_end, uint32_t beg, uint32_t end, void *payload)