Coverage Report

Created: 2026-08-13 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/strings/match.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/match.h"
16
17
#include <algorithm>
18
#include <cstddef>
19
#include <cstdint>
20
21
#include "absl/base/attributes.h"
22
#include "absl/base/config.h"
23
#include "absl/base/internal/endian.h"
24
#include "absl/base/optimization.h"
25
#include "absl/numeric/bits.h"
26
#include "absl/strings/ascii.h"
27
#include "absl/strings/internal/memutil.h"
28
#include "absl/strings/string_view.h"
29
30
namespace absl {
31
ABSL_NAMESPACE_BEGIN
32
33
bool EqualsIgnoreCase(absl::string_view piece1,
34
0
                      absl::string_view piece2) noexcept {
35
0
  return (piece1.size() == piece2.size() &&
36
0
          0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(),
37
0
                                                  piece1.size()));
38
  // memcasecmp uses absl::ascii_tolower().
39
0
}
40
41
namespace {
42
43
// For larger haystacks (n >= 256), Case-Insensitive Boyer-Moore-Horspool
44
// provides sub-linear O(N / M) average-case performance, although it still has
45
// O(N * M) theoretical worst-case scaling.
46
// Scans the haystack from left to right using a search window of size `m`.
47
// For each window offset, Horspool inspects the rightmost character of the
48
// window (`haystack[pos + m - 1]`) first, enabling multi-byte shifts when
49
// mismatches occur and reducing average search time to O(N / M).
50
ABSL_ATTRIBUTE_NOINLINE bool StrContainsIgnoreCaseBMH(
51
0
    absl::string_view haystack, absl::string_view needle) noexcept {
52
0
  const size_t n = haystack.size();
53
0
  const size_t m = needle.size();
54
55
  // Step 1: Initialize the 256-entry shift table.
56
  // Unknown characters default to full window shift of `m` bytes.
57
0
  size_t shift[256];
58
0
  for (size_t i = 0; i < 256; ++i) {
59
0
    shift[i] = m;
60
0
  }
61
62
  // Populate shift distances for needle[0..m-2]. Dual assignment for
63
  // ascii_tolower and ascii_toupper stores distance from rightmost occurrence
64
  // to end of needle.
65
0
  for (size_t i = 0; i < m - 1; ++i) {
66
0
    const unsigned char c = static_cast<unsigned char>(needle[i]);
67
0
    shift[static_cast<unsigned char>(absl::ascii_tolower(c))] = m - 1 - i;
68
0
    shift[static_cast<unsigned char>(absl::ascii_toupper(c))] = m - 1 - i;
69
0
  }
70
71
  // Step 2: Search loop across candidate window offsets.
72
0
  size_t pos = 0;
73
0
  while (pos <= n - m) {
74
0
    const unsigned char last_hay =
75
0
        static_cast<unsigned char>(haystack[pos + m - 1]);
76
0
    const unsigned char last_needle = static_cast<unsigned char>(needle[m - 1]);
77
78
    // Check 1: Inspect right-most character of window first.
79
0
    if (last_hay == last_needle ||
80
0
        absl::ascii_tolower(last_hay) == absl::ascii_tolower(last_needle)) {
81
      // Check 2: Pre-filter on first character of window.
82
0
      const unsigned char first_hay = static_cast<unsigned char>(haystack[pos]);
83
0
      const unsigned char first_needle = static_cast<unsigned char>(needle[0]);
84
0
      if (first_hay == first_needle ||
85
0
          absl::ascii_tolower(first_hay) == absl::ascii_tolower(first_needle)) {
86
        // Check 3: Compare interior (m - 2) bytes.
87
0
        if (EqualsIgnoreCase(haystack.substr(pos + 1, m - 2),
88
0
                             needle.substr(1, m - 2))) {
89
0
          return true;
90
0
        }
91
0
      }
92
0
    }
93
94
    // Advance window by shift distance determined by right-most haystack byte.
95
0
    pos += shift[last_hay];
96
0
  }
97
0
  return false;
98
0
}
99
100
}  // namespace
101
102
bool StrContainsIgnoreCase(absl::string_view haystack,
103
0
                           absl::string_view needle) noexcept {
104
0
  const size_t n = haystack.size();
105
0
  const size_t m = needle.size();
106
0
  if (m == 0) return true;
107
0
  if (n < m) return false;
108
0
  if (m == 1) return StrContainsIgnoreCase(haystack, needle[0]);
109
110
  // For short haystacks (n < 256) or small needles (m == 2), avoid the
111
  // initialization overhead of a 256-entry shift table. Instead, use a fast
112
  // first-and-last character prefilter before inspecting interior bytes.
113
0
  if (n < 256 || m == 2) {
114
0
    const char first_needle =
115
0
        absl::ascii_tolower(static_cast<unsigned char>(needle[0]));
116
0
    const char last_needle =
117
0
        absl::ascii_tolower(static_cast<unsigned char>(needle[m - 1]));
118
119
0
    for (size_t pos = 0; pos <= n - m; ++pos) {
120
0
      const unsigned char first_hay = static_cast<unsigned char>(haystack[pos]);
121
0
      if (absl::ascii_tolower(first_hay) != first_needle) continue;
122
123
0
      const unsigned char last_hay =
124
0
          static_cast<unsigned char>(haystack[pos + m - 1]);
125
0
      if (absl::ascii_tolower(last_hay) != last_needle) continue;
126
127
0
      if (m == 2 || EqualsIgnoreCase(haystack.substr(pos + 1, m - 2),
128
0
                                     needle.substr(1, m - 2))) {
129
0
        return true;
130
0
      }
131
0
    }
132
0
    return false;
133
0
  }
134
135
0
  return StrContainsIgnoreCaseBMH(haystack, needle);
136
0
}
137
138
bool StrContainsIgnoreCase(absl::string_view haystack,
139
0
                           char needle) noexcept {
140
0
  char upper_needle = absl::ascii_toupper(static_cast<unsigned char>(needle));
141
0
  char lower_needle = absl::ascii_tolower(static_cast<unsigned char>(needle));
142
0
  if (upper_needle == lower_needle) {
143
0
    return StrContains(haystack, needle);
144
0
  }
145
0
  if (haystack.size() < 64) {
146
0
    for (char c : haystack) {
147
0
      if (c == lower_needle || c == upper_needle) return true;
148
0
    }
149
0
    return false;
150
0
  }
151
0
  const char both_cstr[3] = {lower_needle, upper_needle, '\0'};
152
0
  return haystack.find_first_of(both_cstr) != absl::string_view::npos;
153
0
}
154
155
bool StartsWithIgnoreCase(absl::string_view text,
156
0
                          absl::string_view prefix) noexcept {
157
0
  return (text.size() >= prefix.size()) &&
158
0
         EqualsIgnoreCase(text.substr(0, prefix.size()), prefix);
159
0
}
160
161
bool EndsWithIgnoreCase(absl::string_view text,
162
0
                        absl::string_view suffix) noexcept {
163
0
  return (text.size() >= suffix.size()) &&
164
0
         EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix);
165
0
}
166
167
absl::string_view FindLongestCommonPrefix(absl::string_view a,
168
0
                                          absl::string_view b) {
169
0
  const absl::string_view::size_type limit = std::min(a.size(), b.size());
170
0
  const char* const pa = a.data();
171
0
  const char* const pb = b.data();
172
0
  absl::string_view::size_type count = (unsigned) 0;
173
174
0
  if (ABSL_PREDICT_FALSE(limit < 8)) {
175
0
    while (ABSL_PREDICT_TRUE(count + 2 <= limit)) {
176
0
      uint16_t xor_bytes = absl::little_endian::Load16(pa + count) ^
177
0
                           absl::little_endian::Load16(pb + count);
178
0
      if (ABSL_PREDICT_FALSE(xor_bytes != 0)) {
179
0
        if (ABSL_PREDICT_TRUE((xor_bytes & 0xff) == 0)) ++count;
180
0
        return absl::string_view(pa, count);
181
0
      }
182
0
      count += 2;
183
0
    }
184
0
    if (ABSL_PREDICT_TRUE(count != limit)) {
185
0
      if (ABSL_PREDICT_TRUE(pa[count] == pb[count])) ++count;
186
0
    }
187
0
    return absl::string_view(pa, count);
188
0
  }
189
190
0
  do {
191
0
    uint64_t xor_bytes = absl::little_endian::Load64(pa + count) ^
192
0
                         absl::little_endian::Load64(pb + count);
193
0
    if (ABSL_PREDICT_FALSE(xor_bytes != 0)) {
194
0
      count += static_cast<uint64_t>(absl::countr_zero(xor_bytes) >> 3);
195
0
      return absl::string_view(pa, count);
196
0
    }
197
0
    count += 8;
198
0
  } while (ABSL_PREDICT_TRUE(count + 8 < limit));
199
200
0
  count = limit - 8;
201
0
  uint64_t xor_bytes = absl::little_endian::Load64(pa + count) ^
202
0
                       absl::little_endian::Load64(pb + count);
203
0
  if (ABSL_PREDICT_TRUE(xor_bytes != 0)) {
204
0
    count += static_cast<uint64_t>(absl::countr_zero(xor_bytes) >> 3);
205
0
    return absl::string_view(pa, count);
206
0
  }
207
0
  return absl::string_view(pa, limit);
208
0
}
209
210
absl::string_view FindLongestCommonSuffix(absl::string_view a,
211
0
                                          absl::string_view b) {
212
0
  const absl::string_view::size_type limit = std::min(a.size(), b.size());
213
0
  if (limit == 0) return absl::string_view();
214
215
0
  const char* pa = a.data() + a.size() - 1;
216
0
  const char* pb = b.data() + b.size() - 1;
217
0
  absl::string_view::size_type count = (unsigned) 0;
218
0
  while (count < limit && *pa == *pb) {
219
0
    --pa;
220
0
    --pb;
221
0
    ++count;
222
0
  }
223
224
0
  return absl::string_view(++pa, count);
225
0
}
226
227
ABSL_NAMESPACE_END
228
}  // namespace absl