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/string_view.h"
16
17 #ifndef ABSL_USES_STD_STRING_VIEW
18
19 #include <algorithm>
20 #include <climits>
21 #include <cstring>
22 #include <ostream>
23
24 #include "absl/base/nullability.h"
25
26 namespace absl {
27 ABSL_NAMESPACE_BEGIN
28
29 namespace {
30
31 // This is significantly faster for case-sensitive matches with very
32 // few possible matches.
memmatch(absl::Nullable<const char * > phaystack,size_t haylen,absl::Nullable<const char * > pneedle,size_t neelen)33 absl::Nullable<const char*> memmatch(absl::Nullable<const char*> phaystack,
34 size_t haylen,
35 absl::Nullable<const char*> pneedle,
36 size_t neelen) {
37 if (0 == neelen) {
38 return phaystack; // even if haylen is 0
39 }
40 if (haylen < neelen) return nullptr;
41
42 const char* match;
43 const char* hayend = phaystack + haylen - neelen + 1;
44 // A static cast is used here as memchr returns a const void *, and pointer
45 // arithmetic is not allowed on pointers to void.
46 while (
47 (match = static_cast<const char*>(memchr(
48 phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) {
49 if (memcmp(match, pneedle, neelen) == 0)
50 return match;
51 else
52 phaystack = match + 1;
53 }
54 return nullptr;
55 }
56
WritePadding(std::ostream & o,size_t pad)57 void WritePadding(std::ostream& o, size_t pad) {
58 char fill_buf[32];
59 memset(fill_buf, o.fill(), sizeof(fill_buf));
60 while (pad) {
61 size_t n = std::min(pad, sizeof(fill_buf));
62 o.write(fill_buf, static_cast<std::streamsize>(n));
63 pad -= n;
64 }
65 }
66
67 class LookupTable {
68 public:
69 // For each character in wanted, sets the index corresponding
70 // to the ASCII code of that character. This is used by
71 // the find_.*_of methods below to tell whether or not a character is in
72 // the lookup table in constant time.
LookupTable(string_view wanted)73 explicit LookupTable(string_view wanted) {
74 for (char c : wanted) {
75 table_[Index(c)] = true;
76 }
77 }
operator [](char c) const78 bool operator[](char c) const { return table_[Index(c)]; }
79
80 private:
Index(char c)81 static unsigned char Index(char c) { return static_cast<unsigned char>(c); }
82 bool table_[UCHAR_MAX + 1] = {};
83 };
84
85 } // namespace
86
operator <<(std::ostream & o,string_view piece)87 std::ostream& operator<<(std::ostream& o, string_view piece) {
88 std::ostream::sentry sentry(o);
89 if (sentry) {
90 size_t lpad = 0;
91 size_t rpad = 0;
92 if (static_cast<size_t>(o.width()) > piece.size()) {
93 size_t pad = static_cast<size_t>(o.width()) - piece.size();
94 if ((o.flags() & o.adjustfield) == o.left) {
95 rpad = pad;
96 } else {
97 lpad = pad;
98 }
99 }
100 if (lpad) WritePadding(o, lpad);
101 o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
102 if (rpad) WritePadding(o, rpad);
103 o.width(0);
104 }
105 return o;
106 }
107
find(string_view s,size_type pos) const108 string_view::size_type string_view::find(string_view s,
109 size_type pos) const noexcept {
110 if (empty() || pos > length_) {
111 if (empty() && pos == 0 && s.empty()) return 0;
112 return npos;
113 }
114 const char* result = memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_);
115 return result ? static_cast<size_type>(result - ptr_) : npos;
116 }
117
find(char c,size_type pos) const118 string_view::size_type string_view::find(char c, size_type pos) const noexcept {
119 if (empty() || pos >= length_) {
120 return npos;
121 }
122 const char* result =
123 static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos));
124 return result != nullptr ? static_cast<size_type>(result - ptr_) : npos;
125 }
126
rfind(string_view s,size_type pos) const127 string_view::size_type string_view::rfind(string_view s,
128 size_type pos) const noexcept {
129 if (length_ < s.length_) return npos;
130 if (s.empty()) return std::min(length_, pos);
131 const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
132 const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
133 return result != last ? static_cast<size_type>(result - ptr_) : npos;
134 }
135
136 // Search range is [0..pos] inclusive. If pos == npos, search everything.
rfind(char c,size_type pos) const137 string_view::size_type string_view::rfind(char c,
138 size_type pos) const noexcept {
139 // Note: memrchr() is not available on Windows.
140 if (empty()) return npos;
141 for (size_type i = std::min(pos, length_ - 1);; --i) {
142 if (ptr_[i] == c) {
143 return i;
144 }
145 if (i == 0) break;
146 }
147 return npos;
148 }
149
find_first_of(string_view s,size_type pos) const150 string_view::size_type string_view::find_first_of(
151 string_view s, size_type pos) const noexcept {
152 if (empty() || s.empty()) {
153 return npos;
154 }
155 // Avoid the cost of LookupTable() for a single-character search.
156 if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
157 LookupTable tbl(s);
158 for (size_type i = pos; i < length_; ++i) {
159 if (tbl[ptr_[i]]) {
160 return i;
161 }
162 }
163 return npos;
164 }
165
find_first_not_of(string_view s,size_type pos) const166 string_view::size_type string_view::find_first_not_of(
167 string_view s, size_type pos) const noexcept {
168 if (empty()) return npos;
169 // Avoid the cost of LookupTable() for a single-character search.
170 if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
171 LookupTable tbl(s);
172 for (size_type i = pos; i < length_; ++i) {
173 if (!tbl[ptr_[i]]) {
174 return i;
175 }
176 }
177 return npos;
178 }
179
find_first_not_of(char c,size_type pos) const180 string_view::size_type string_view::find_first_not_of(
181 char c, size_type pos) const noexcept {
182 if (empty()) return npos;
183 for (; pos < length_; ++pos) {
184 if (ptr_[pos] != c) {
185 return pos;
186 }
187 }
188 return npos;
189 }
190
find_last_of(string_view s,size_type pos) const191 string_view::size_type string_view::find_last_of(string_view s,
192 size_type pos) const noexcept {
193 if (empty() || s.empty()) return npos;
194 // Avoid the cost of LookupTable() for a single-character search.
195 if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
196 LookupTable tbl(s);
197 for (size_type i = std::min(pos, length_ - 1);; --i) {
198 if (tbl[ptr_[i]]) {
199 return i;
200 }
201 if (i == 0) break;
202 }
203 return npos;
204 }
205
find_last_not_of(string_view s,size_type pos) const206 string_view::size_type string_view::find_last_not_of(
207 string_view s, size_type pos) const noexcept {
208 if (empty()) return npos;
209 size_type i = std::min(pos, length_ - 1);
210 if (s.empty()) return i;
211 // Avoid the cost of LookupTable() for a single-character search.
212 if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
213 LookupTable tbl(s);
214 for (;; --i) {
215 if (!tbl[ptr_[i]]) {
216 return i;
217 }
218 if (i == 0) break;
219 }
220 return npos;
221 }
222
find_last_not_of(char c,size_type pos) const223 string_view::size_type string_view::find_last_not_of(
224 char c, size_type pos) const noexcept {
225 if (empty()) return npos;
226 size_type i = std::min(pos, length_ - 1);
227 for (;; --i) {
228 if (ptr_[i] != c) {
229 return i;
230 }
231 if (i == 0) break;
232 }
233 return npos;
234 }
235
236 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
237 constexpr string_view::size_type string_view::npos;
238 constexpr string_view::size_type string_view::kMaxSize;
239 #endif
240
241 ABSL_NAMESPACE_END
242 } // namespace absl
243
244 #else
245
246 // https://github.com/abseil/abseil-cpp/issues/1465
247 // CMake builds on Apple platforms error when libraries are empty.
248 // Our CMake configuration can avoid this error on header-only libraries,
249 // but since this library is conditionally empty, including a single
250 // variable is an easy workaround.
251 #ifdef __APPLE__
252 namespace absl {
253 ABSL_NAMESPACE_BEGIN
254 namespace strings_internal {
255 extern const char kAvoidEmptyStringViewLibraryWarning;
256 const char kAvoidEmptyStringViewLibraryWarning = 0;
257 } // namespace strings_internal
258 ABSL_NAMESPACE_END
259 } // namespace absl
260 #endif // __APPLE__
261
262 #endif // ABSL_USES_STD_STRING_VIEW
263