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