File: sort.c

package info (click to toggle)
libjodycode 3.1-3~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 356 kB
  • sloc: ansic: 1,536; makefile: 170; sh: 155; xml: 9
file content (107 lines) | stat: -rw-r--r-- 3,215 bytes parent folder | download
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;
}