Skip to content

Commit

Permalink
O(n) sorting for selection markers: sort_selection()
Browse files Browse the repository at this point in the history
QuickSort produces O(nlog n) at best and O(n^2) at worst. Sort selection
markers with a trivial radix sort algorithm, which guarantees O(n) time.
As a consequence both time and space consumption will be linear.

Do not generalize the algorithm, as other sites most likely require a
comparison based sorting algorithm. For those sites the goal should be
to eliminate the worst case O(n^2) and have a steady O(nlog n)
performance.

Signed-off-by: Jarkko Sakkinen <[email protected]>
  • Loading branch information
jarkkojs committed Jun 28, 2024
1 parent 86d883e commit f6904ae
Showing 1 changed file with 40 additions and 8 deletions.
48 changes: 40 additions & 8 deletions src/nnn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1724,12 +1724,40 @@ static char *findinsel(char *startpos, int len)
}
}

static int markcmp(const void *va, const void *vb)
{
const selmark *ma = (selmark*)va;
const selmark *mb = (selmark*)vb;
/*
* Sort selection markers by their starting position in O(n) time. Use one byte
* radix and eight passes (starting from the LSB).
*/
static void sort_marked(selmark *data, selmark *scratch, size_t nelem)
{
size_t cnt[256];
size_t idx[256];
size_t value;
size_t i, j;
void *tmp;
int b;

for (b = 0; b < 8; b++) {
memset(cnt, 0, sizeof(cnt));
for (i = 0; i < nelem; i++) {
value = (size_t)data[i].startpos;
cnt[(value >> (b * 8)) & 0xFF]++;
}

return ma->startpos - mb->startpos;
idx[0] = 0;
for (i = 1; i < 256; i++)
idx[i] = idx[i - 1] + cnt[i - 1];

for (i = 0; i < nelem; i++) {
value = (size_t)data[i].startpos;
j = idx[(value >> (b * 8)) & 0xFF]++;
memcpy(&scratch[j], &data[i], sizeof(selmark));
}

tmp = data;
data = scratch;
scratch = tmp;
}
}

/* scanselforpath() must be called before calling this */
Expand All @@ -1754,10 +1782,13 @@ static void invertselbuf(const int pathlen)
int i, nmarked = 0, prev = 0;
struct entry *dentp;
bool scan = FALSE;
selmark *marked = malloc(nselected * sizeof(selmark));
selmark *marked = calloc(nselected, sizeof(*marked));
selmark *scratch = calloc(nselected, sizeof(*scratch));

if (!marked) {
if (marked == NULL || scratch == NULL) {
printwarn(NULL);
free(marked);
free(scratch);
return;
}

Expand Down Expand Up @@ -1810,7 +1841,7 @@ static void invertselbuf(const int pathlen)
* With entries sorted we can merge adjacent ones allowing us to
* move them in a single go.
*/
qsort(marked, nmarked, sizeof(selmark), &markcmp);
sort_marked(marked, scratch, nselected);

/* Some files might be adjacent. Merge them into a single entry */
for (i = 1; i < nmarked; ++i) {
Expand Down Expand Up @@ -1850,6 +1881,7 @@ static void invertselbuf(const int pathlen)
}

free(marked);
free(scratch);

/* Buffer size adjustment */
selbufpos -= shrinklen;
Expand Down

0 comments on commit f6904ae

Please sign in to comment.