Ardour  9.0-pre0-582-g084a23a80d
natsort.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #ifndef PBD_NATSORT
20 #define PBD_NATSORT
21 
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <stdint.h>
25 
26 namespace PBD {
27 
28 inline bool
29 is_integer (const char* i)
30 {
31  return isdigit (*i) || (*i == '-' && isdigit (i[1]));
32 }
33 
34 /* return scale factor for SI metric prefix x 1000
35  * (to allow milli for integers)
36  */
37 inline int64_t
38 order_of_magnitude (const char* i)
39 {
40  if (!is_integer (i)) {
41  return 0;
42  }
43  while (isdigit (*++i)) ;
44  if (!*i) {
45  return 1e3;
46  }
47  switch (*i) {
48  case 'm':
49  return 1;
50  case 'c':
51  return 10;
52  case 'd':
53  return 100;
54  case 'k':
55  /* fallthrough */
56  case 'K':
57  return 1e6;
58  case 'M':
59  return 1e9;
60  case 'G':
61  return 1e12;
62  case 'T':
63  return 1e15;
64  }
65  return 1e3;
66 }
67 
68 /* this method sorts negative integers before
69  * positive ones, and also handles hexadecimal
70  * numbers when prefixed with "0x" or "0X".
71  * SI metric prefixes for integers are handled.
72  * (floating point, and rational numbers are
73  * not directy handled)
74  */
75 inline bool
76 numerically_less (const char* a, const char* b)
77 {
78  const char* d_a = NULL;
79  const char* d_b = NULL;
80 
81  for (;*a && *b; ++a, ++b) {
82  if (is_integer (a) && is_integer (b) && !d_a) {
83  d_a = a; d_b = b;
84  continue;
85  }
86  if (d_a) {
87  /* strip leading zeros to prevent `strtol` from using octal */
88  while (*d_a == '0') { if (d_a[1] && isdigit (d_a[1])) { ++d_a; } else { break; } }
89  while (*d_b == '0') { if (d_b[1] && isdigit (d_b[1])) { ++d_b; } else { break; } }
90 
91  const int64_t ia = strtol (d_a, NULL, 0) * order_of_magnitude (d_a);
92  const int64_t ib = strtol (d_b, NULL, 0) * order_of_magnitude (d_b);
93  if (ia != ib) {
94  return ia < ib;
95  }
96  }
97  d_a = d_b = NULL;
98  if (*a == *b) {
99  continue;
100  }
101  return *a < *b;
102  }
103 
104  if (d_a) {
105  return strtol (d_a, NULL, 0) * order_of_magnitude (d_a) < strtol (d_b, NULL, 0) * order_of_magnitude (d_b);
106  }
107 
108  /* if we reach here, either strings are same length and equal
109  * or one is longer than the other.
110  */
111 
112  if (*a) { return false; }
113  if (*b) { return true; }
114  return false; // equal
115 }
116 
117 inline int
118 natcmp (const char* a, const char* b)
119 {
120  const char* d_a = NULL;
121  const char* d_b = NULL;
122 
123  for (;*a && *b; ++a, ++b) {
124  if (isdigit (*a) && isdigit (*b) && !d_a) {
125  d_a = a; d_b = b;
126  continue;
127  }
128  if (d_a) {
129  const int ia = atoi (d_a);
130  const int ib = atoi (d_b);
131  if (ia != ib) {
132  return ia < ib ? -1 : 1;
133  }
134  }
135  d_a = d_b = NULL;
136  if (*a == *b) {
137  continue;
138  }
139 #if 1
140  /* treat underscore as space, this works around idiosyncratic
141  * ffado port-names: "foo_in", "foo0_in", "foo2_in", etc */
142  if (*a == '_' && *b == ' ') {
143  continue;
144  }
145  if (*b == '_' && *a == ' ') {
146  continue;
147  }
148  if (*a == '_') {
149  return ' ' < *b ? -1 : 1;
150  } else if (*b == '_') {
151  return *a < ' ' ? -1 : 1;
152  } else
153 #endif
154  return *a < *b ? -1 : 1;
155  }
156 
157  if (d_a) {
158  const int ia = atoi (d_a);
159  const int ib = atoi (d_b);
160  if (ia != ib) {
161  return ia < ib ? -1 : 1;
162  }
163  }
164 
165  /* if we reach here, either strings are same length and equal
166  * or one is longer than the other.
167  */
168  if (*a) { return 1; }
169  if (*b) { return -1; }
170  return 0;
171 }
172 
173 inline bool
174 naturally_less (const char* a, const char* b)
175 {
176  return natcmp (a, b) < 0;
177 }
178 
179 } // namespace PBD
180 
181 #endif // PBD_NATSORT
Definition: axis_view.h:42
bool naturally_less(const char *a, const char *b)
Definition: natsort.h:174
int atoi(const std::string &)
int natcmp(const char *a, const char *b)
Definition: natsort.h:118
int64_t order_of_magnitude(const char *i)
Definition: natsort.h:38
bool numerically_less(const char *a, const char *b)
Definition: natsort.h:76
bool is_integer(const char *i)
Definition: natsort.h:29