blob: 0d6e35d8a5e111bd7e4072ff3f8815b453270ec9 [file] [log] [blame]
Ed Tanousa8544a52021-09-29 14:31:13 -07001#pragma once
2
Ed Tanousa8544a52021-09-29 14:31:13 -07003#include <string_view>
4
5namespace details
6{
7
8// This implementation avoids the complexity of using std::isdigit, which pulls
9// in all of <locale>, and likely has other consequences.
10inline bool simpleIsDigit(const char c)
11{
12 return c >= '0' && c <= '9';
13}
14
15} // namespace details
16
17inline int alphanumComp(const std::string_view left,
18 const std::string_view right)
19{
20 enum class ModeType
21 {
22 STRING,
23 NUMBER
24 } mode = ModeType::STRING;
25
26 std::string_view::const_iterator l = left.begin();
27 std::string_view::const_iterator r = right.begin();
28
29 while (l != left.end() && r != right.end())
30 {
31 if (mode == ModeType::STRING)
32 {
33 while (l != left.end() && r != right.end())
34 {
35 // check if this are digit characters
36 const bool lDigit = details::simpleIsDigit(*l);
37 const bool rDigit = details::simpleIsDigit(*r);
38 // if both characters are digits, we continue in NUMBER mode
39 if (lDigit && rDigit)
40 {
41 mode = ModeType::NUMBER;
42 break;
43 }
44 // if only the left character is a digit, we have a result
45 if (lDigit)
46 {
47 return -1;
48 } // if only the right character is a digit, we have a result
49 if (rDigit)
50 {
51 return +1;
52 }
53 // compute the difference of both characters
54 const int diff = *l - *r;
55 // if they differ we have a result
56 if (diff != 0)
57 {
58 return diff;
59 }
60 // otherwise process the next characters
61 l++;
62 r++;
63 }
64 }
65 else // mode==NUMBER
66 {
67 // get the left number
68 int lInt = 0;
69 while (l != left.end() && details::simpleIsDigit(*l))
70 {
71 lInt = lInt * 10 + static_cast<int>(*l) - '0';
72 ++l;
73 }
74
75 // get the right number
76 int rInt = 0;
77 while (r != right.end() && details::simpleIsDigit(*r))
78 {
79 rInt = rInt * 10 + static_cast<int>(*r) - '0';
80 ++r;
81 }
82
83 // if the difference is not equal to zero, we have a comparison
84 // result
85 const int diff = lInt - rInt;
86 if (diff != 0)
87 {
88 return diff;
89 }
90
91 // otherwise we process the next substring in STRING mode
92 mode = ModeType::STRING;
93 }
94 }
95 if (r == right.end() && l == left.end())
96 {
97 return 0;
98 }
99 if (r == right.end())
100 {
101 return 1;
102 }
103 return -1;
104}
105
106// A generic template type compatible with std::less that can be used on generic
107// containers (set, map, ect)
108template <class Type>
109struct AlphanumLess
110{
111 bool operator()(const Type& left, const Type& right) const
112 {
113 return alphanumComp(left, right) < 0;
114 }
115};