1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
/* Jody Bruchon's sorting code library
*
* Copyright (C) 2014-2023 by Jody Bruchon <jody@jodybruchon.com>
* Released under The MIT License
*/
#include <stdlib.h>
#include "likely_unlikely.h"
#include "libjodycode.h"
#define IS_NUM(a) (((a >= '0') && (a <= '9')) ? 1 : 0)
#define IS_LOWER(a) (((a >= 'a') && (a <= 'z')) ? 1 : 0)
/* Sort by logical numeric order (10 comes after 2, not before) */
extern int jc_numeric_sort(char * restrict c1,
char * restrict c2, int sort_direction)
{
int len1 = 0, len2 = 0;
int precompare;
char *rewind1, *rewind2;
if (unlikely(c1 == NULL || c2 == NULL)) return -99;
/* Numerically correct sort */
while (unlikely(*c1 != '\0' && *c2 != '\0')) {
/* Reset string length counters and rewind points */
len1 = 0; len2 = 0; rewind1 = c1; rewind2 = c2;
/* Skip all sequences of zeroes */
while (*c1 == '0') {
len1++;
c1++;
}
while (*c2 == '0') {
len2++;
c2++;
}
/* If both chars are numeric, do a numeric comparison */
if (IS_NUM(*c1) && IS_NUM(*c2)) {
precompare = 0;
/* Scan numbers and get preliminary results */
while (IS_NUM(*c1) && IS_NUM(*c2)) {
if (*c1 < *c2) precompare = -sort_direction;
if (*c1 > *c2) precompare = sort_direction;
len1++; len2++;
c1++; c2++;
/* Skip remaining digit pairs after any
* difference is found */
if (precompare != 0) {
while (IS_NUM(*c1) && IS_NUM(*c2)) {
len1++; len2++;
c1++; c2++;
}
break;
}
}
/* One numeric and one non-numeric means the
* numeric one is larger and sorts later */
if (IS_NUM(*c1) ^ IS_NUM(*c2)) {
if (IS_NUM(*c1)) return sort_direction;
else return -sort_direction;
}
/* If the last test fell through, numbers are
* of equal length. Use the precompare result
* as the result for this number comparison. */
if (precompare != 0) return precompare;
} else {
/* Zeroes aren't followed by a digit; rewind the streams */
c1 = rewind1; c2 = rewind2;
len1 = 0; len2 = 0;
}
/* Do normal comparison */
if (likely(*c1 == *c2 && *c1 != '\0' && *c2 != '\0')) {
c1++; c2++;
len1++; len2++;
/* Put symbols and spaces after everything else */
} else if (*c2 < '.' && *c1 >= '.') return -sort_direction;
else if (*c1 < '.' && *c2 >= '.') return sort_direction;
/* Normal strcasecmp() style compare */
else {
char s1 = *c1, s2 = *c2;
/* Convert lowercase into uppercase */
if (IS_LOWER(s1)) s1 = (char)(s1 - 32);
if (IS_LOWER(s2)) s2 = (char)(s2 - 32);
if (s1 > s2) return sort_direction;
else return -sort_direction;
}
}
/* Longer strings generally sort later */
if (len1 < len2) return -sort_direction;
if (len1 > len2) return sort_direction;
/* Normal comparison - FIXME? length check should already handle these */
if (unlikely(*c1 == '\0' && *c2 != '\0')) return -sort_direction;
if (unlikely(*c1 != '\0' && *c2 == '\0')) return sort_direction;
/* Fall through: the strings are equal */
return 0;
}
|