Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmStringAlgorithms.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmStringAlgorithms.h"
4
5
#include <algorithm>
6
#include <cerrno>
7
#include <cstddef> // IWYU pragma: keep
8
#include <cstdio>
9
#include <cstdlib>
10
#include <limits>
11
12
#include "cmsys/String.h"
13
14
bool cmStrCaseEq(cm::string_view s1, cm::string_view s2)
15
17.1k
{
16
17.1k
  if (s1.size() != s2.size()) {
17
16.9k
    return false;
18
16.9k
  }
19
20
274
  return std::equal(s1.begin(), s1.end(), s2.begin(), [](char a, char b) {
21
274
    return cmsysString_tolower(a) == cmsysString_tolower(b);
22
274
  });
23
17.1k
}
24
25
std::string cmTrimWhitespace(cm::string_view str)
26
1.50k
{
27
  // XXX(clang-tidy): This declaration and the next cannot be `const auto*`
28
  // because the qualification of `auto` is platform-dependent.
29
  // NOLINTNEXTLINE(readability-qualified-auto)
30
1.50k
  auto start = str.begin();
31
119k
  while (start != str.end() && cmsysString_isspace(*start)) {
32
118k
    ++start;
33
118k
  }
34
1.50k
  if (start == str.end()) {
35
30
    return std::string();
36
30
  }
37
  // NOLINTNEXTLINE(readability-qualified-auto)
38
1.47k
  auto stop = str.end() - 1;
39
19.2k
  while (cmsysString_isspace(*stop)) {
40
17.7k
    --stop;
41
17.7k
  }
42
1.47k
  return std::string(start, stop + 1);
43
1.50k
}
44
45
cm::string_view cmStripWhitespace(cm::string_view str)
46
0
{
47
0
  std::string::size_type const l = str.size();
48
49
0
  std::string::size_type s = 0;
50
0
  while (s < l && cmsysString_isspace(str[s])) {
51
0
    ++s;
52
0
  }
53
0
  if (s == l) {
54
0
    return cm::string_view{};
55
0
  }
56
0
  std::string::size_type e = l - 1;
57
0
  while (cmsysString_isspace(str[e])) {
58
0
    --e;
59
0
  }
60
0
  return str.substr(s, e + 1 - s);
61
0
}
62
63
std::string cmRemoveQuotes(cm::string_view str)
64
1.50k
{
65
  // We process only strings that have two quotes at least.
66
  // Also front() and back() are only defined behavior on non empty strings.
67
1.50k
  if (str.size() >= 2 &&    //
68
1.45k
      str.front() == '"' && //
69
62
      str.back() == '"') {
70
    // Remove a quote from the front and back
71
13
    str.remove_prefix(1);
72
13
    str.remove_suffix(1);
73
13
  }
74
1.50k
  return std::string(str);
75
1.50k
}
76
77
std::string cmEscapeQuotes(cm::string_view str)
78
1.50k
{
79
1.50k
  std::string result;
80
1.50k
  result.reserve(str.size());
81
1.81M
  for (char const ch : str) {
82
1.81M
    if (ch == '"') {
83
36.2k
      result += '\\';
84
36.2k
    }
85
1.81M
    result += ch;
86
1.81M
  }
87
1.50k
  return result;
88
1.50k
}
89
90
std::size_t cmLevenshteinDistance(cm::string_view a, cm::string_view b)
91
0
{
92
0
  if (a == b) {
93
0
    return 0;
94
0
  }
95
96
0
  std::size_t const aSize = a.size();
97
0
  std::size_t const bSize = b.size();
98
0
  if (aSize == 0) {
99
0
    return bSize;
100
0
  }
101
0
  if (bSize == 0) {
102
0
    return aSize;
103
0
  }
104
105
0
  std::vector<std::size_t> previous(bSize + 1, 0);
106
0
  std::vector<std::size_t> current(bSize + 1, 0);
107
108
0
  for (std::size_t j = 0; j <= bSize; ++j) {
109
0
    previous[j] = j;
110
0
  }
111
112
0
  for (std::size_t i = 1; i <= aSize; ++i) {
113
0
    current[0] = i;
114
0
    for (std::size_t j = 1; j <= bSize; ++j) {
115
0
      std::size_t const substitutionCost = (a[i - 1] == b[j - 1]) ? 0 : 1;
116
0
      current[j] = std::min({ previous[j] + 1, current[j - 1] + 1,
117
0
                              previous[j - 1] + substitutionCost });
118
0
    }
119
0
    previous.swap(current);
120
0
  }
121
122
0
  return previous[bSize];
123
0
}
124
125
std::string cmFindClosestString(cm::string_view input,
126
                                std::vector<std::string> const& candidates)
127
0
{
128
0
  if (candidates.empty()) {
129
0
    return std::string();
130
0
  }
131
132
0
  std::string best;
133
0
  std::size_t bestDistance = std::numeric_limits<std::size_t>::max();
134
0
  for (std::string const& candidate : candidates) {
135
0
    std::size_t const distance = cmLevenshteinDistance(input, candidate);
136
0
    if (distance < bestDistance) {
137
0
      bestDistance = distance;
138
0
      best = candidate;
139
0
    }
140
0
  }
141
142
  // Scale the acceptable edit distance with the input length (roughly one
143
  // typo per 5 characters), but keep it within [kMinDistance, kMaxDistance]
144
  // so very short inputs still allow one edit and very long inputs don't
145
  // start accepting barely-related matches.
146
0
  std::size_t const kMinDistance = 1;
147
0
  std::size_t const kMaxDistance = 4;
148
0
  std::size_t const maxDistance =
149
0
    std::min(kMaxDistance, std::max(kMinDistance, input.size() / 5 + 1));
150
0
  if (bestDistance > maxDistance) {
151
0
    return std::string();
152
0
  }
153
154
0
  return best;
155
0
}
156
157
namespace {
158
template <std::size_t N, typename T>
159
inline void MakeDigits(cm::string_view& view, char (&digits)[N],
160
                       char const* pattern, T value)
161
1.04M
{
162
1.04M
  int res = std::snprintf(digits, N, pattern, value);
163
1.04M
  if (res > 0 && res < static_cast<int>(N)) {
164
1.04M
    view = cm::string_view(digits, static_cast<std::size_t>(res));
165
1.04M
  }
166
1.04M
}
cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, int>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, int)
Line
Count
Source
161
998k
{
162
998k
  int res = std::snprintf(digits, N, pattern, value);
163
998k
  if (res > 0 && res < static_cast<int>(N)) {
164
998k
    view = cm::string_view(digits, static_cast<std::size_t>(res));
165
998k
  }
166
998k
}
cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, unsigned int>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, unsigned int)
Line
Count
Source
161
62
{
162
62
  int res = std::snprintf(digits, N, pattern, value);
163
62
  if (res > 0 && res < static_cast<int>(N)) {
164
62
    view = cm::string_view(digits, static_cast<std::size_t>(res));
165
62
  }
166
62
}
cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, long>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, long)
Line
Count
Source
161
15.3k
{
162
15.3k
  int res = std::snprintf(digits, N, pattern, value);
163
15.3k
  if (res > 0 && res < static_cast<int>(N)) {
164
15.3k
    view = cm::string_view(digits, static_cast<std::size_t>(res));
165
15.3k
  }
166
15.3k
}
cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, unsigned long>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, unsigned long)
Line
Count
Source
161
26.4k
{
162
26.4k
  int res = std::snprintf(digits, N, pattern, value);
163
26.4k
  if (res > 0 && res < static_cast<int>(N)) {
164
26.4k
    view = cm::string_view(digits, static_cast<std::size_t>(res));
165
26.4k
  }
166
26.4k
}
Unexecuted instantiation: cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, long long>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, long long)
Unexecuted instantiation: cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, unsigned long long>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, unsigned long long)
Unexecuted instantiation: cmStringAlgorithms.cxx:void (anonymous namespace)::MakeDigits<32ul, double>(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, char (&) [32ul], char const*, double)
167
} // unnamed namespace
168
169
cmAlphaNum::cmAlphaNum(int val)
170
998k
{
171
998k
  MakeDigits(this->View_, this->Digits_, "%i", val);
172
998k
}
173
174
cmAlphaNum::cmAlphaNum(unsigned int val)
175
62
{
176
62
  MakeDigits(this->View_, this->Digits_, "%u", val);
177
62
}
178
179
cmAlphaNum::cmAlphaNum(long int val)
180
15.3k
{
181
15.3k
  MakeDigits(this->View_, this->Digits_, "%li", val);
182
15.3k
}
183
184
cmAlphaNum::cmAlphaNum(unsigned long int val)
185
26.4k
{
186
26.4k
  MakeDigits(this->View_, this->Digits_, "%lu", val);
187
26.4k
}
188
189
cmAlphaNum::cmAlphaNum(long long int val)
190
0
{
191
0
  MakeDigits(this->View_, this->Digits_, "%lli", val);
192
0
}
193
194
cmAlphaNum::cmAlphaNum(unsigned long long int val)
195
0
{
196
0
  MakeDigits(this->View_, this->Digits_, "%llu", val);
197
0
}
198
199
cmAlphaNum::cmAlphaNum(float val)
200
0
{
201
0
  MakeDigits(this->View_, this->Digits_, "%g", static_cast<double>(val));
202
0
}
203
204
cmAlphaNum::cmAlphaNum(double val)
205
0
{
206
0
  MakeDigits(this->View_, this->Digits_, "%g", val);
207
0
}
208
209
std::string cmCatViews(
210
  std::initializer_list<std::pair<cm::string_view, std::string*>> views)
211
7.16M
{
212
7.16M
  std::size_t totalSize = 0;
213
7.16M
  std::string* rvalueString = nullptr;
214
7.16M
  std::size_t rvalueStringLength = 0;
215
7.16M
  std::size_t rvalueStringOffset = 0;
216
16.8M
  for (auto const& view : views) {
217
    // Find the rvalue string with the largest capacity.
218
16.8M
    if (view.second &&
219
1.00M
        (!rvalueString ||
220
900k
         view.second->capacity() > rvalueString->capacity())) {
221
900k
      rvalueString = view.second;
222
900k
      rvalueStringLength = rvalueString->length();
223
900k
      rvalueStringOffset = totalSize;
224
900k
    }
225
16.8M
    totalSize += view.first.size();
226
16.8M
  }
227
228
7.16M
  std::string result;
229
7.16M
  std::string::size_type initialLen = 0;
230
7.16M
  if (rvalueString && rvalueString->capacity() >= totalSize) {
231
214k
    result = std::move(*rvalueString);
232
6.95M
  } else {
233
6.95M
    rvalueString = nullptr;
234
6.95M
  }
235
7.16M
  result.resize(totalSize);
236
7.16M
  if (rvalueString && rvalueStringOffset > 0) {
237
73.0k
    std::copy_backward(result.begin(), result.begin() + rvalueStringLength,
238
73.0k
                       result.begin() + rvalueStringOffset +
239
73.0k
                         rvalueStringLength);
240
73.0k
  }
241
7.16M
  std::string::iterator sit = result.begin() + initialLen;
242
16.8M
  for (auto const& view : views) {
243
16.8M
    if (rvalueString && view.second == rvalueString) {
244
214k
      sit += rvalueStringLength;
245
16.6M
    } else {
246
16.6M
      sit = std::copy_n(view.first.data(), view.first.size(), sit);
247
16.6M
    }
248
16.8M
  }
249
7.16M
  return result;
250
7.16M
}
251
252
bool cmStrToLong(char const* str, long* value)
253
0
{
254
0
  errno = 0;
255
0
  char* endp;
256
0
  *value = strtol(str, &endp, 10);
257
0
  return (*endp == '\0') && (endp != str) && (errno == 0);
258
0
}
259
260
bool cmStrToLong(std::string const& str, long* value)
261
0
{
262
0
  return cmStrToLong(str.c_str(), value);
263
0
}
264
265
bool cmStrToULong(char const* str, unsigned long* value)
266
0
{
267
0
  errno = 0;
268
0
  char* endp;
269
0
  while (cmsysString_isspace(*str)) {
270
0
    ++str;
271
0
  }
272
0
  if (*str == '-') {
273
0
    return false;
274
0
  }
275
0
  *value = strtoul(str, &endp, 10);
276
0
  return (*endp == '\0') && (endp != str) && (errno == 0);
277
0
}
278
279
bool cmStrToULong(std::string const& str, unsigned long* value)
280
0
{
281
0
  return cmStrToULong(str.c_str(), value);
282
0
}
283
284
bool cmStrToLongLong(char const* str, long long* value)
285
0
{
286
0
  errno = 0;
287
0
  char* endp;
288
0
  *value = strtoll(str, &endp, 10);
289
0
  return (*endp == '\0') && (endp != str) && (errno == 0);
290
0
}
291
292
bool cmStrToLongLong(std::string const& str, long long* value)
293
0
{
294
0
  return cmStrToLongLong(str.c_str(), value);
295
0
}
296
297
bool cmStrToULongLong(char const* str, unsigned long long* value)
298
0
{
299
0
  errno = 0;
300
0
  char* endp;
301
0
  while (cmsysString_isspace(*str)) {
302
0
    ++str;
303
0
  }
304
0
  if (*str == '-') {
305
0
    return false;
306
0
  }
307
0
  *value = strtoull(str, &endp, 10);
308
0
  return (*endp == '\0') && (endp != str) && (errno == 0);
309
0
}
310
311
bool cmStrToULongLong(std::string const& str, unsigned long long* value)
312
0
{
313
0
  return cmStrToULongLong(str.c_str(), value);
314
0
}
315
316
std::string cmJoin(std::vector<std::string> const& rng,
317
                   cm::string_view separator, cm::string_view initial)
318
33.8k
{
319
33.8k
  return cmJoinStrings(rng, separator, initial);
320
33.8k
}
321
322
std::string cmJoin(cmStringRange rng, cm::string_view separator,
323
                   cm::string_view initial)
324
1.59k
{
325
1.59k
  return cmJoinStrings(rng, separator, initial);
326
1.59k
}