Coverage Report

Created: 2026-10-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/strings/ascii.cc
Line
Count
Source
1
// Copyright 2017 The Abseil Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "absl/strings/ascii.h"
16
17
#include <algorithm>
18
#include <climits>
19
#include <cstddef>
20
#include <cstring>
21
#include <string>
22
23
#include "absl/base/config.h"
24
#include "absl/base/nullability.h"
25
#include "absl/strings/resize_and_overwrite.h"
26
#include "absl/strings/string_view.h"
27
28
namespace absl {
29
ABSL_NAMESPACE_BEGIN
30
namespace ascii_internal {
31
32
// # Table generated by this Python code (bit 0x02 is currently unused):
33
// TODO(mbar) Move Python code for generation of table to BUILD and link here.
34
35
// NOTE: The kAsciiPropertyBits table used within this code was generated by
36
// Python code of the following form. (Bit 0x02 is currently unused and
37
// available.)
38
//
39
// def Hex2(n):
40
//   return '0x' + hex(n/16)[2:] + hex(n%16)[2:]
41
// def IsPunct(ch):
42
//   return (ord(ch) >= 32 and ord(ch) < 127 and
43
//           not ch.isspace() and not ch.isalnum())
44
// def IsBlank(ch):
45
//   return ch in ' \t'
46
// def IsCntrl(ch):
47
//   return ord(ch) < 32 or ord(ch) == 127
48
// def IsXDigit(ch):
49
//   return ch.isdigit() or ch.lower() in 'abcdef'
50
// for i in range(128):
51
//   ch = chr(i)
52
//   mask = ((ch.isalpha() and 0x01 or 0) |
53
//           (ch.isalnum() and 0x04 or 0) |
54
//           (ch.isspace() and 0x08 or 0) |
55
//           (IsPunct(ch) and 0x10 or 0) |
56
//           (IsBlank(ch) and 0x20 or 0) |
57
//           (IsCntrl(ch) and 0x40 or 0) |
58
//           (IsXDigit(ch) and 0x80 or 0))
59
//   print Hex2(mask) + ',',
60
//   if i % 16 == 7:
61
//     print ' //', Hex2(i & 0x78)
62
//   elif i % 16 == 15:
63
//     print
64
65
// clang-format off
66
// Array of bitfields holding character information. Each bit value corresponds
67
// to a particular character feature. For readability, and because the value
68
// of these bits is tightly coupled to this implementation, the individual bits
69
// are not named. Note that bitfields for all characters above ASCII 127 are
70
// zero-initialized.
71
ABSL_DLL const unsigned char kPropertyBits[256] = {
72
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  // 0x00
73
    0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40,
74
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  // 0x10
75
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
76
    0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,  // 0x20
77
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
78
    0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,  // 0x30
79
    0x84, 0x84, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
80
    0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05,  // 0x40
81
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
82
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,  // 0x50
83
    0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x10,
84
    0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05,  // 0x60
85
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
86
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,  // 0x70
87
    0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x40,
88
};
89
90
// Array of characters for the ascii_tolower() function. For values 'A'
91
// through 'Z', return the lower-case character; otherwise, return the
92
// identity of the passed character.
93
ABSL_DLL const char kToLower[256] = {
94
  '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
95
  '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
96
  '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
97
  '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
98
  '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
99
  '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
100
  '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
101
  '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
102
  '\x40',    'a',    'b',    'c',    'd',    'e',    'f',    'g',
103
     'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
104
     'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
105
     'x',    'y',    'z', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
106
  '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
107
  '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
108
  '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
109
  '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f',
110
  '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
111
  '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
112
  '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
113
  '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
114
  '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
115
  '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
116
  '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
117
  '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
118
  '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
119
  '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
120
  '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
121
  '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
122
  '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
123
  '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
124
  '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
125
  '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff',
126
};
127
128
// Array of characters for the ascii_toupper() function. For values 'a'
129
// through 'z', return the upper-case character; otherwise, return the
130
// identity of the passed character.
131
ABSL_DLL const char kToUpper[256] = {
132
  '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
133
  '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
134
  '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
135
  '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
136
  '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
137
  '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
138
  '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
139
  '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
140
  '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
141
  '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
142
  '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
143
  '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
144
  '\x60',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
145
     'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
146
     'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
147
     'X',    'Y',    'Z', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f',
148
  '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
149
  '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
150
  '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
151
  '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
152
  '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
153
  '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
154
  '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
155
  '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
156
  '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
157
  '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
158
  '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
159
  '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
160
  '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
161
  '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
162
  '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
163
  '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff',
164
};
165
// clang-format on
166
167
// Returns whether `c` is in the a-z/A-Z range (w.r.t. `ToUpper`).
168
// Implemented by:
169
//  1. Pushing the a-z/A-Z range to [SCHAR_MIN, SCHAR_MIN + 26).
170
//  2. Comparing to SCHAR_MIN + 26.
171
template <bool ToUpper>
172
0
constexpr bool AsciiInAZRange(unsigned char c) {
173
0
  constexpr unsigned char sub = (ToUpper ? 'a' : 'A') - SCHAR_MIN;
174
0
  constexpr signed char threshold = SCHAR_MIN + 26;  // 26 = alphabet size.
175
  // Using unsigned arithmetic as overflows/underflows are well defined.
176
0
  unsigned char u = c - sub;
177
  // Using signed cmp, as SIMD unsigned cmp isn't available in many platforms.
178
0
  return static_cast<signed char>(u) < threshold;
179
0
}
Unexecuted instantiation: bool absl::ascii_internal::AsciiInAZRange<false>(unsigned char)
Unexecuted instantiation: bool absl::ascii_internal::AsciiInAZRange<true>(unsigned char)
180
181
template <bool ToUpper>
182
0
constexpr bool AsciiInAZRangeNaive(unsigned char c) {
183
0
  constexpr unsigned char a = (ToUpper ? 'a' : 'A');
184
0
  constexpr unsigned char z = (ToUpper ? 'z' : 'Z');
185
0
  return a <= c && c <= z;
186
0
}
Unexecuted instantiation: bool absl::ascii_internal::AsciiInAZRangeNaive<false>(unsigned char)
Unexecuted instantiation: bool absl::ascii_internal::AsciiInAZRangeNaive<true>(unsigned char)
187
188
template <bool ToUpper, bool Naive>
189
constexpr void AsciiStrCaseFoldImpl(char* absl_nonnull dst,
190
                                    const char* absl_nullable src,
191
0
                                    size_t size) {
192
  // The upper- and lowercase versions of ASCII characters differ by only 1 bit.
193
  // When we need to flip the case, we can xor with this bit to achieve the
194
  // desired result. Note that the choice of 'a' and 'A' here is arbitrary. We
195
  // could have chosen 'z' and 'Z', or any other pair of characters as they all
196
  // have the same single bit difference.
197
0
  constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A';
198
199
0
  for (size_t i = 0; i < size; ++i) {
200
0
    unsigned char v = static_cast<unsigned char>(src[i]);
201
0
    if constexpr (Naive) {
202
0
      v ^= AsciiInAZRangeNaive<ToUpper>(v) ? kAsciiCaseBitFlip : 0;
203
0
    } else {
204
0
      v ^= AsciiInAZRange<ToUpper>(v) ? kAsciiCaseBitFlip : 0;
205
0
    }
206
0
    dst[i] = static_cast<char>(v);
207
0
  }
208
0
}
Unexecuted instantiation: void absl::ascii_internal::AsciiStrCaseFoldImpl<false, true>(char*, char const*, unsigned long)
Unexecuted instantiation: void absl::ascii_internal::AsciiStrCaseFoldImpl<false, false>(char*, char const*, unsigned long)
Unexecuted instantiation: void absl::ascii_internal::AsciiStrCaseFoldImpl<true, true>(char*, char const*, unsigned long)
Unexecuted instantiation: void absl::ascii_internal::AsciiStrCaseFoldImpl<true, false>(char*, char const*, unsigned long)
209
210
// Splitting to short and long strings to allow vectorization decisions
211
// to be made separately in the long and short cases.
212
// Using slightly different implementations so the compiler won't optimize them
213
// into the same code (the non-naive version is needed for SIMD, so for short
214
// strings it's not important).
215
// `src` may be null iff `size` is zero.
216
template <bool ToUpper>
217
constexpr void AsciiStrCaseFold(char* absl_nonnull dst,
218
0
                                const char* absl_nullable src, size_t size) {
219
0
  size < 16 ? AsciiStrCaseFoldImpl<ToUpper, /*Naive=*/true>(dst, src, size)
220
0
            : AsciiStrCaseFoldImpl<ToUpper, /*Naive=*/false>(dst, src, size);
221
0
}
Unexecuted instantiation: void absl::ascii_internal::AsciiStrCaseFold<false>(char*, char const*, unsigned long)
Unexecuted instantiation: void absl::ascii_internal::AsciiStrCaseFold<true>(char*, char const*, unsigned long)
222
223
void AsciiStrToLower(char* absl_nonnull dst, const char* absl_nullable src,
224
0
                     size_t n) {
225
0
  return AsciiStrCaseFold<false>(dst, src, n);
226
0
}
227
228
void AsciiStrToUpper(char* absl_nonnull dst, const char* absl_nullable src,
229
0
                     size_t n) {
230
0
  return AsciiStrCaseFold<true>(dst, src, n);
231
0
}
232
233
0
static constexpr size_t ValidateAsciiCasefold() {
234
0
  constexpr size_t num_chars = 1 + CHAR_MAX - CHAR_MIN;
235
0
  size_t incorrect_index = 0;
236
0
  char lowered[num_chars] = {};
237
0
  char uppered[num_chars] = {};
238
0
  for (unsigned int i = 0; i < num_chars; ++i) {
239
0
    uppered[i] = lowered[i] = static_cast<char>(i);
240
0
  }
241
0
  AsciiStrCaseFold<false>(&lowered[0], &lowered[0], num_chars);
242
0
  AsciiStrCaseFold<true>(&uppered[0], &uppered[0], num_chars);
243
0
  for (size_t i = 0; i < num_chars; ++i) {
244
0
    const char ch = static_cast<char>(i),
245
0
               ch_upper = ('a' <= ch && ch <= 'z' ? 'A' + (ch - 'a') : ch),
246
0
               ch_lower = ('A' <= ch && ch <= 'Z' ? 'a' + (ch - 'A') : ch);
247
0
    if (uppered[i] != ch_upper || lowered[i] != ch_lower) {
248
0
      incorrect_index = i > 0 ? i : num_chars;
249
0
      break;
250
0
    }
251
0
  }
252
0
  return incorrect_index;
253
0
}
254
255
static_assert(ValidateAsciiCasefold() == 0, "error in case conversion");
256
257
}  // namespace ascii_internal
258
259
0
void AsciiStrToLower(std::string* absl_nonnull s) {
260
0
  char* p = &(*s)[0];
261
0
  return ascii_internal::AsciiStrCaseFold<false>(p, p, s->size());
262
0
}
263
264
0
std::string AsciiStrToLower(absl::string_view s) {
265
0
  std::string result;
266
0
  StringResizeAndOverwrite(result, s.size(), [s](char* buf, size_t buf_size) {
267
0
    ascii_internal::AsciiStrToLower(buf, s.data(), s.size());
268
0
    return buf_size;
269
0
  });
270
0
  return result;
271
0
}
272
273
0
void AsciiStrToUpper(std::string* absl_nonnull s) {
274
0
  char* p = &(*s)[0];
275
0
  return ascii_internal::AsciiStrCaseFold<true>(p, p, s->size());
276
0
}
277
278
0
std::string AsciiStrToUpper(absl::string_view s) {
279
0
  std::string result;
280
0
  StringResizeAndOverwrite(result, s.size(), [s](char* buf, size_t buf_size) {
281
0
    ascii_internal::AsciiStrToUpper(buf, s.data(), s.size());
282
0
    return buf_size;
283
0
  });
284
0
  return result;
285
0
}
286
287
0
absl::string_view StripLeadingAsciiWhitespace(absl::string_view str) {
288
0
  auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
289
0
  return str.substr(static_cast<size_t>(it - str.begin()));
290
0
}
291
292
0
void StripLeadingAsciiWhitespace(std::string* absl_nonnull str) {
293
0
  auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
294
0
  str->erase(str->begin(), it);
295
0
}
296
297
0
absl::string_view StripTrailingAsciiWhitespace(absl::string_view str) {
298
0
  auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
299
0
  return str.substr(0, static_cast<size_t>(str.rend() - it));
300
0
}
301
302
0
void StripTrailingAsciiWhitespace(std::string* absl_nonnull str) {
303
0
  auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
304
0
  str->erase(static_cast<size_t>(str->rend() - it));
305
0
}
306
307
0
absl::string_view StripAsciiWhitespace(absl::string_view str) {
308
0
  return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
309
0
}
310
311
0
void StripAsciiWhitespace(std::string* absl_nonnull str) {
312
0
  StripTrailingAsciiWhitespace(str);
313
0
  StripLeadingAsciiWhitespace(str);
314
0
}
315
316
0
void RemoveExtraAsciiWhitespace(std::string* absl_nonnull str) {
317
0
  auto stripped = StripAsciiWhitespace(*str);
318
319
0
  if (stripped.empty()) {
320
0
    str->clear();
321
0
    return;
322
0
  }
323
324
0
  auto input_it = stripped.begin();
325
0
  auto input_end = stripped.end();
326
0
  auto output_it = &(*str)[0];
327
0
  bool is_ws = false;
328
329
0
  for (; input_it < input_end; ++input_it) {
330
0
    if (is_ws) {
331
      // Consecutive whitespace?  Keep only the last.
332
0
      is_ws = absl::ascii_isspace(static_cast<unsigned char>(*input_it));
333
0
      if (is_ws) --output_it;
334
0
    } else {
335
0
      is_ws = absl::ascii_isspace(static_cast<unsigned char>(*input_it));
336
0
    }
337
338
0
    *output_it = *input_it;
339
0
    ++output_it;
340
0
  }
341
342
0
  str->erase(static_cast<size_t>(output_it - &(*str)[0]));
343
0
}
344
345
ABSL_NAMESPACE_END
346
}  // namespace absl