xref: /openbmc/bmcweb/include/human_sort.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3a8544a5bSEd Tanous #pragma once
4a8544a5bSEd Tanous 
5bb05f222SEd Tanous #include <charconv>
6d5c80ad9SNan Zhou #include <iterator>
7a8544a5bSEd Tanous #include <string_view>
8a8544a5bSEd Tanous 
9a8544a5bSEd Tanous namespace details
10a8544a5bSEd Tanous {
11a8544a5bSEd Tanous 
12a8544a5bSEd Tanous // This implementation avoids the complexity of using std::isdigit, which pulls
13a8544a5bSEd Tanous // in all of <locale>, and likely has other consequences.
simpleIsDigit(const char c)14a8544a5bSEd Tanous inline bool simpleIsDigit(const char c)
15a8544a5bSEd Tanous {
16a8544a5bSEd Tanous     return c >= '0' && c <= '9';
17a8544a5bSEd Tanous }
18a8544a5bSEd Tanous 
19bb05f222SEd Tanous enum class ModeType
20bb05f222SEd Tanous {
21bb05f222SEd Tanous     STRING,
22bb05f222SEd Tanous     NUMBER
23bb05f222SEd Tanous };
24bb05f222SEd Tanous 
25a8544a5bSEd Tanous } // namespace details
26a8544a5bSEd Tanous 
alphanumComp(std::string_view left,std::string_view right)2726ccae32SEd Tanous inline int alphanumComp(std::string_view left, std::string_view right)
28a8544a5bSEd Tanous {
29d13f468cSPatrick Williams     std::string_view::const_iterator l = left.cbegin();
30d13f468cSPatrick Williams     std::string_view::const_iterator r = right.cbegin();
31a8544a5bSEd Tanous 
32bb05f222SEd Tanous     details::ModeType mode = details::ModeType::STRING;
33bb05f222SEd Tanous 
34a8544a5bSEd Tanous     while (l != left.end() && r != right.end())
35a8544a5bSEd Tanous     {
36bb05f222SEd Tanous         if (mode == details::ModeType::STRING)
37a8544a5bSEd Tanous         {
38a8544a5bSEd Tanous             // check if this are digit characters
39a8544a5bSEd Tanous             const bool lDigit = details::simpleIsDigit(*l);
40a8544a5bSEd Tanous             const bool rDigit = details::simpleIsDigit(*r);
41a8544a5bSEd Tanous             // if both characters are digits, we continue in NUMBER mode
42a8544a5bSEd Tanous             if (lDigit && rDigit)
43a8544a5bSEd Tanous             {
44bb05f222SEd Tanous                 mode = details::ModeType::NUMBER;
45bb05f222SEd Tanous                 continue;
46a8544a5bSEd Tanous             }
47a8544a5bSEd Tanous             // if only the left character is a digit, we have a result
48a8544a5bSEd Tanous             if (lDigit)
49a8544a5bSEd Tanous             {
50a8544a5bSEd Tanous                 return -1;
51a8544a5bSEd Tanous             } // if only the right character is a digit, we have a result
52a8544a5bSEd Tanous             if (rDigit)
53a8544a5bSEd Tanous             {
54a8544a5bSEd Tanous                 return +1;
55a8544a5bSEd Tanous             }
56a8544a5bSEd Tanous             // compute the difference of both characters
57a8544a5bSEd Tanous             const int diff = *l - *r;
58a8544a5bSEd Tanous             // if they differ we have a result
59a8544a5bSEd Tanous             if (diff != 0)
60a8544a5bSEd Tanous             {
61a8544a5bSEd Tanous                 return diff;
62a8544a5bSEd Tanous             }
63a8544a5bSEd Tanous             // otherwise process the next characters
64d13f468cSPatrick Williams             std::advance(l, 1);
65d13f468cSPatrick Williams             std::advance(r, 1);
66a8544a5bSEd Tanous         }
67a8544a5bSEd Tanous         else // mode==NUMBER
68a8544a5bSEd Tanous         {
69a8544a5bSEd Tanous             // get the left number
70a8544a5bSEd Tanous             int lInt = 0;
71bb05f222SEd Tanous             auto fc = std::from_chars(&(*l), &(*left.end()), lInt);
72d13f468cSPatrick Williams             std::advance(l, std::distance(l, fc.ptr));
73a8544a5bSEd Tanous 
74a8544a5bSEd Tanous             // get the right number
75a8544a5bSEd Tanous             int rInt = 0;
76bb05f222SEd Tanous             fc = std::from_chars(&(*r), &(*right.end()), rInt);
77d13f468cSPatrick Williams             std::advance(r, std::distance(r, fc.ptr));
78a8544a5bSEd Tanous 
79a8544a5bSEd Tanous             // if the difference is not equal to zero, we have a comparison
80a8544a5bSEd Tanous             // result
81a8544a5bSEd Tanous             const int diff = lInt - rInt;
82a8544a5bSEd Tanous             if (diff != 0)
83a8544a5bSEd Tanous             {
84a8544a5bSEd Tanous                 return diff;
85a8544a5bSEd Tanous             }
86a8544a5bSEd Tanous 
87a8544a5bSEd Tanous             // otherwise we process the next substring in STRING mode
88bb05f222SEd Tanous             mode = details::ModeType::STRING;
89a8544a5bSEd Tanous         }
90a8544a5bSEd Tanous     }
91a8544a5bSEd Tanous     if (r == right.end() && l == left.end())
92a8544a5bSEd Tanous     {
93a8544a5bSEd Tanous         return 0;
94a8544a5bSEd Tanous     }
95a8544a5bSEd Tanous     if (r == right.end())
96a8544a5bSEd Tanous     {
97a8544a5bSEd Tanous         return 1;
98a8544a5bSEd Tanous     }
99a8544a5bSEd Tanous     return -1;
100a8544a5bSEd Tanous }
101a8544a5bSEd Tanous 
102a8544a5bSEd Tanous // A generic template type compatible with std::less that can be used on generic
1038ece0e45SEd Tanous // containers (set, map, etc)
104a8544a5bSEd Tanous template <class Type>
105a8544a5bSEd Tanous struct AlphanumLess
106a8544a5bSEd Tanous {
operator ()AlphanumLess107a8544a5bSEd Tanous     bool operator()(const Type& left, const Type& right) const
108a8544a5bSEd Tanous     {
109a8544a5bSEd Tanous         return alphanumComp(left, right) < 0;
110a8544a5bSEd Tanous     }
111a8544a5bSEd Tanous };
112