/src/abseil-cpp/absl/strings/string_view.cc
Line | Count | Source (jump to first uncovered line) |
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 | | namespace absl { |
25 | | ABSL_NAMESPACE_BEGIN |
26 | | |
27 | | namespace { |
28 | | |
29 | | // This is significantly faster for case-sensitive matches with very |
30 | | // few possible matches. |
31 | | const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, |
32 | 0 | size_t neelen) { |
33 | 0 | if (0 == neelen) { |
34 | 0 | return phaystack; // even if haylen is 0 |
35 | 0 | } |
36 | 0 | if (haylen < neelen) return nullptr; |
37 | | |
38 | 0 | const char* match; |
39 | 0 | const char* hayend = phaystack + haylen - neelen + 1; |
40 | | // A static cast is used here as memchr returns a const void *, and pointer |
41 | | // arithmetic is not allowed on pointers to void. |
42 | 0 | while ( |
43 | 0 | (match = static_cast<const char*>(memchr( |
44 | 0 | phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) { |
45 | 0 | if (memcmp(match, pneedle, neelen) == 0) |
46 | 0 | return match; |
47 | 0 | else |
48 | 0 | phaystack = match + 1; |
49 | 0 | } |
50 | 0 | return nullptr; |
51 | 0 | } |
52 | | |
53 | 0 | void WritePadding(std::ostream& o, size_t pad) { |
54 | 0 | char fill_buf[32]; |
55 | 0 | memset(fill_buf, o.fill(), sizeof(fill_buf)); |
56 | 0 | while (pad) { |
57 | 0 | size_t n = std::min(pad, sizeof(fill_buf)); |
58 | 0 | o.write(fill_buf, static_cast<std::streamsize>(n)); |
59 | 0 | pad -= n; |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | | class LookupTable { |
64 | | public: |
65 | | // For each character in wanted, sets the index corresponding |
66 | | // to the ASCII code of that character. This is used by |
67 | | // the find_.*_of methods below to tell whether or not a character is in |
68 | | // the lookup table in constant time. |
69 | 40 | explicit LookupTable(string_view wanted) { |
70 | 80 | for (char c : wanted) { |
71 | 80 | table_[Index(c)] = true; |
72 | 80 | } |
73 | 40 | } |
74 | 80 | bool operator[](char c) const { return table_[Index(c)]; } |
75 | | |
76 | | private: |
77 | 160 | static unsigned char Index(char c) { return static_cast<unsigned char>(c); } |
78 | | bool table_[UCHAR_MAX + 1] = {}; |
79 | | }; |
80 | | |
81 | | } // namespace |
82 | | |
83 | 0 | std::ostream& operator<<(std::ostream& o, string_view piece) { |
84 | 0 | std::ostream::sentry sentry(o); |
85 | 0 | if (sentry) { |
86 | 0 | size_t lpad = 0; |
87 | 0 | size_t rpad = 0; |
88 | 0 | if (static_cast<size_t>(o.width()) > piece.size()) { |
89 | 0 | size_t pad = static_cast<size_t>(o.width()) - piece.size(); |
90 | 0 | if ((o.flags() & o.adjustfield) == o.left) { |
91 | 0 | rpad = pad; |
92 | 0 | } else { |
93 | 0 | lpad = pad; |
94 | 0 | } |
95 | 0 | } |
96 | 0 | if (lpad) WritePadding(o, lpad); |
97 | 0 | o.write(piece.data(), static_cast<std::streamsize>(piece.size())); |
98 | 0 | if (rpad) WritePadding(o, rpad); |
99 | 0 | o.width(0); |
100 | 0 | } |
101 | 0 | return o; |
102 | 0 | } |
103 | | |
104 | | string_view::size_type string_view::find(string_view s, |
105 | 0 | size_type pos) const noexcept { |
106 | 0 | if (empty() || pos > length_) { |
107 | 0 | if (empty() && pos == 0 && s.empty()) return 0; |
108 | 0 | return npos; |
109 | 0 | } |
110 | 0 | const char* result = memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_); |
111 | 0 | return result ? static_cast<size_type>(result - ptr_) : npos; |
112 | 0 | } |
113 | | |
114 | 44.4M | string_view::size_type string_view::find(char c, size_type pos) const noexcept { |
115 | 44.4M | if (empty() || pos >= length_) { |
116 | 6.13M | return npos; |
117 | 6.13M | } |
118 | 38.3M | const char* result = |
119 | 38.3M | static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos)); |
120 | 38.3M | return result != nullptr ? static_cast<size_type>(result - ptr_) : npos; |
121 | 44.4M | } |
122 | | |
123 | | string_view::size_type string_view::rfind(string_view s, |
124 | 0 | size_type pos) const noexcept { |
125 | 0 | if (length_ < s.length_) return npos; |
126 | 0 | if (s.empty()) return std::min(length_, pos); |
127 | 0 | const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; |
128 | 0 | const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); |
129 | 0 | return result != last ? static_cast<size_type>(result - ptr_) : npos; |
130 | 0 | } |
131 | | |
132 | | // Search range is [0..pos] inclusive. If pos == npos, search everything. |
133 | | string_view::size_type string_view::rfind(char c, |
134 | 3.24M | size_type pos) const noexcept { |
135 | | // Note: memrchr() is not available on Windows. |
136 | 3.24M | if (empty()) return npos; |
137 | 38.9M | for (size_type i = std::min(pos, length_ - 1);; --i) { |
138 | 38.9M | if (ptr_[i] == c) { |
139 | 3.24M | return i; |
140 | 3.24M | } |
141 | 35.6M | if (i == 0) break; |
142 | 35.6M | } |
143 | 0 | return npos; |
144 | 3.24M | } |
145 | | |
146 | | string_view::size_type string_view::find_first_of( |
147 | 0 | string_view s, size_type pos) const noexcept { |
148 | 0 | if (empty() || s.empty()) { |
149 | 0 | return npos; |
150 | 0 | } |
151 | | // Avoid the cost of LookupTable() for a single-character search. |
152 | 0 | if (s.length_ == 1) return find_first_of(s.ptr_[0], pos); |
153 | 0 | LookupTable tbl(s); |
154 | 0 | for (size_type i = pos; i < length_; ++i) { |
155 | 0 | if (tbl[ptr_[i]]) { |
156 | 0 | return i; |
157 | 0 | } |
158 | 0 | } |
159 | 0 | return npos; |
160 | 0 | } |
161 | | |
162 | | string_view::size_type string_view::find_first_not_of( |
163 | 40 | string_view s, size_type pos) const noexcept { |
164 | 40 | if (empty()) return npos; |
165 | | // Avoid the cost of LookupTable() for a single-character search. |
166 | 40 | if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos); |
167 | 40 | LookupTable tbl(s); |
168 | 80 | for (size_type i = pos; i < length_; ++i) { |
169 | 80 | if (!tbl[ptr_[i]]) { |
170 | 40 | return i; |
171 | 40 | } |
172 | 80 | } |
173 | 0 | return npos; |
174 | 40 | } |
175 | | |
176 | | string_view::size_type string_view::find_first_not_of( |
177 | 0 | char c, size_type pos) const noexcept { |
178 | 0 | if (empty()) return npos; |
179 | 0 | for (; pos < length_; ++pos) { |
180 | 0 | if (ptr_[pos] != c) { |
181 | 0 | return pos; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | return npos; |
185 | 0 | } |
186 | | |
187 | | string_view::size_type string_view::find_last_of(string_view s, |
188 | 0 | size_type pos) const noexcept { |
189 | 0 | if (empty() || s.empty()) return npos; |
190 | | // Avoid the cost of LookupTable() for a single-character search. |
191 | 0 | if (s.length_ == 1) return find_last_of(s.ptr_[0], pos); |
192 | 0 | LookupTable tbl(s); |
193 | 0 | for (size_type i = std::min(pos, length_ - 1);; --i) { |
194 | 0 | if (tbl[ptr_[i]]) { |
195 | 0 | return i; |
196 | 0 | } |
197 | 0 | if (i == 0) break; |
198 | 0 | } |
199 | 0 | return npos; |
200 | 0 | } |
201 | | |
202 | | string_view::size_type string_view::find_last_not_of( |
203 | 0 | string_view s, size_type pos) const noexcept { |
204 | 0 | if (empty()) return npos; |
205 | 0 | size_type i = std::min(pos, length_ - 1); |
206 | 0 | if (s.empty()) return i; |
207 | | // Avoid the cost of LookupTable() for a single-character search. |
208 | 0 | if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos); |
209 | 0 | LookupTable tbl(s); |
210 | 0 | for (;; --i) { |
211 | 0 | if (!tbl[ptr_[i]]) { |
212 | 0 | return i; |
213 | 0 | } |
214 | 0 | if (i == 0) break; |
215 | 0 | } |
216 | 0 | return npos; |
217 | 0 | } |
218 | | |
219 | | string_view::size_type string_view::find_last_not_of( |
220 | 0 | char c, size_type pos) const noexcept { |
221 | 0 | if (empty()) return npos; |
222 | 0 | size_type i = std::min(pos, length_ - 1); |
223 | 0 | for (;; --i) { |
224 | 0 | if (ptr_[i] != c) { |
225 | 0 | return i; |
226 | 0 | } |
227 | 0 | if (i == 0) break; |
228 | 0 | } |
229 | 0 | return npos; |
230 | 0 | } |
231 | | |
232 | | #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL |
233 | | constexpr string_view::size_type string_view::npos; |
234 | | constexpr string_view::size_type string_view::kMaxSize; |
235 | | #endif |
236 | | |
237 | | ABSL_NAMESPACE_END |
238 | | } // namespace absl |
239 | | |
240 | | #endif // ABSL_USES_STD_STRING_VIEW |