summaryrefslogtreecommitdiff
path: root/libs/pbd
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2019-12-29 19:47:48 +0100
committerRobin Gareus <robin@gareus.org>2019-12-29 19:47:48 +0100
commit04a409df6f73e7eecaba31cfe6478e245638d8c0 (patch)
tree11f02dd10bc61579f673ac735f6299e62b4d3bf3 /libs/pbd
parented67f465fb765d33ebe4a459540b371bbae44a95 (diff)
Add a numerically_less compare operator
This is similar to sort(1) --human-numeric-sort, as opposed to naturally_less() negative numbers, hex-prefixes and SI metric prefixes are taken into account.
Diffstat (limited to 'libs/pbd')
-rw-r--r--libs/pbd/pbd/natsort.h87
1 files changed, 86 insertions, 1 deletions
diff --git a/libs/pbd/pbd/natsort.h b/libs/pbd/pbd/natsort.h
index 5d62b0ef89..e96d682147 100644
--- a/libs/pbd/pbd/natsort.h
+++ b/libs/pbd/pbd/natsort.h
@@ -25,6 +25,91 @@
namespace PBD {
inline bool
+is_integer (const char* i)
+{
+ return isdigit (*i) || (*i == '-' && isdigit (i[1]));
+}
+
+/* return scale factor for SI metric prefix x 1000
+ * (to allow milli for integers)
+ */
+inline int64_t
+order_of_magnitude (const char* i)
+{
+ if (!is_integer (i)) {
+ return 0;
+ }
+ while (isdigit (*++i)) ;
+ if (!*i) {
+ return 1e3;
+ }
+ switch (*i) {
+ case 'm':
+ return 1;
+ case 'c':
+ return 10;
+ case 'd':
+ return 100;
+ case 'k':
+ /* fallthrough */
+ case 'K':
+ return 1e6;
+ case 'M':
+ return 1e9;
+ case 'G':
+ return 1e12;
+ case 'T':
+ return 1e15;
+ }
+ return 1e3;
+}
+
+/* this method sorts negative integers before
+ * positive ones, and also handles hexadecimal
+ * numbers when prefixed with "0x" or "0X".
+ * SI metric prefixes for integers are handled.
+ * (floating point, and rational numbers are
+ * not directy handled)
+ */
+inline bool
+numerically_less (const char* a, const char* b)
+{
+ const char* d_a = NULL;
+ const char* d_b = NULL;
+
+ for (;*a && *b; ++a, ++b) {
+ if (is_integer (a) && is_integer (b) && !d_a) {
+ d_a = a; d_b = b;
+ continue;
+ }
+ if (d_a) {
+ const int64_t ia = strtoll (d_a, NULL, 0) * order_of_magnitude (d_a);
+ const int64_t ib = strtoll (d_b, NULL, 0) * order_of_magnitude (d_b);
+ if (ia != ib) {
+ return ia < ib;
+ }
+ }
+ d_a = d_b = NULL;
+ if (*a == *b) {
+ continue;
+ }
+ return *a < *b;
+ }
+
+ if (d_a) {
+ return strtoll (d_a, NULL, 0) * order_of_magnitude (d_a) < strtoll (d_b, NULL, 0) * order_of_magnitude (d_b);
+ }
+
+ /* if we reach here, either strings are same length and equal
+ * or one is longer than the other.
+ */
+
+ if (*a) { return false; }
+ if (*b) { return true; }
+ return false; // equal
+}
+
+inline bool
naturally_less (const char* a, const char* b)
{
const char* d_a = NULL;
@@ -45,7 +130,7 @@ naturally_less (const char* a, const char* b)
d_a = d_b = NULL;
if (*a == *b) {
continue;
- }
+ }
return *a < *b;
}