Ardour  9.7-53-gdd292e0e94
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 #include <string>
26 
27 namespace PBD {
28 
29 inline bool
30 is_integer (const char* i)
31 {
32  return isdigit (*i) || (*i == '-' && isdigit (i[1]));
33 }
34 
35 /* return scale factor for SI metric prefix x 1000
36  * (to allow milli for integers)
37  */
38 inline int64_t
39 order_of_magnitude (const char* i)
40 {
41  if (!is_integer (i)) {
42  return 0;
43  }
44  while (isdigit (*++i)) ;
45  if (!*i) {
46  return 1e3;
47  }
48  switch (*i) {
49  case 'm':
50  return 1;
51  case 'c':
52  return 10;
53  case 'd':
54  return 100;
55  case 'k':
56  /* fallthrough */
57  case 'K':
58  return 1e6;
59  case 'M':
60  return 1e9;
61  case 'G':
62  return 1e12;
63  case 'T':
64  return 1e15;
65  }
66  return 1e3;
67 }
68 
69 /* this method sorts negative integers before
70  * positive ones, and also handles hexadecimal
71  * numbers when prefixed with "0x" or "0X".
72  * SI metric prefixes for integers are handled.
73  * (floating point, and rational numbers are
74  * not directy handled)
75  */
76 inline bool
77 numerically_less (const char* a, const char* b)
78 {
79  const char* d_a = NULL;
80  const char* d_b = NULL;
81 
82  for (;*a && *b; ++a, ++b) {
83  if (is_integer (a) && is_integer (b) && !d_a) {
84  d_a = a; d_b = b;
85  continue;
86  }
87  if (d_a) {
88  /* strip leading zeros to prevent `strtol` from using octal */
89  while (*d_a == '0') { if (d_a[1] && isdigit (d_a[1])) { ++d_a; } else { break; } }
90  while (*d_b == '0') { if (d_b[1] && isdigit (d_b[1])) { ++d_b; } else { break; } }
91 
92  const int64_t ia = strtol (d_a, NULL, 0) * order_of_magnitude (d_a);
93  const int64_t ib = strtol (d_b, NULL, 0) * order_of_magnitude (d_b);
94  if (ia != ib) {
95  return ia < ib;
96  }
97  }
98  d_a = d_b = NULL;
99  if (*a == *b) {
100  continue;
101  }
102  return *a < *b;
103  }
104 
105  if (d_a) {
106  return strtol (d_a, NULL, 0) * order_of_magnitude (d_a) < strtol (d_b, NULL, 0) * order_of_magnitude (d_b);
107  }
108 
109  /* if we reach here, either strings are same length and equal
110  * or one is longer than the other.
111  */
112 
113  if (*a) { return false; }
114  if (*b) { return true; }
115  return false; // equal
116 }
117 
118 inline int
119 natcmp (const char* a, const char* b)
120 {
121  const char* d_a = NULL;
122  const char* d_b = NULL;
123 
124  for (;*a && *b; ++a, ++b) {
125  if (isdigit (*a) && isdigit (*b) && !d_a) {
126  d_a = a; d_b = b;
127  continue;
128  }
129  if (d_a) {
130  const int ia = atoi (d_a);
131  const int ib = atoi (d_b);
132  if (ia != ib) {
133  return ia < ib ? -1 : 1;
134  }
135  }
136  d_a = d_b = NULL;
137  if (*a == *b) {
138  continue;
139  }
140 #if 1
141  /* treat underscore as space, this works around idiosyncratic
142  * ffado port-names: "foo_in", "foo0_in", "foo2_in", etc */
143  if (*a == '_' && *b == ' ') {
144  continue;
145  }
146  if (*b == '_' && *a == ' ') {
147  continue;
148  }
149  if (*a == '_') {
150  return ' ' < *b ? -1 : 1;
151  } else if (*b == '_') {
152  return *a < ' ' ? -1 : 1;
153  } else
154 #endif
155  return *a < *b ? -1 : 1;
156  }
157 
158  if (d_a) {
159  const int ia = atoi (d_a);
160  const int ib = atoi (d_b);
161  if (ia != ib) {
162  return ia < ib ? -1 : 1;
163  }
164  }
165 
166  /* if we reach here, either strings are same length and equal
167  * or one is longer than the other.
168  */
169  if (*a) { return 1; }
170  if (*b) { return -1; }
171  return 0;
172 }
173 
174 inline bool
175 naturally_less (const char* a, const char* b)
176 {
177  return natcmp (a, b) < 0;
178 }
179 
180 inline bool
181 naturally_less (const std::string& a, const std::string& b)
182 {
183  const char* cstr_a = a.c_str();
184  const char* cstr_b = b.c_str();
185 
186  return naturally_less (cstr_a, cstr_b);
187 }
188 
189 } // namespace PBD
190 
191 #endif // PBD_NATSORT
Definition: axis_view.h:42
bool naturally_less(const char *a, const char *b)
Definition: natsort.h:175
int atoi(const std::string &)
int natcmp(const char *a, const char *b)
Definition: natsort.h:119
int64_t order_of_magnitude(const char *i)
Definition: natsort.h:39
bool numerically_less(const char *a, const char *b)
Definition: natsort.h:77
bool is_integer(const char *i)
Definition: natsort.h:30