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.
memmatch(const char * phaystack,size_t haylen,const char * pneedle,size_t neelen)31 const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
32 size_t neelen) {
33 if (0 == neelen) {
34 return phaystack; // even if haylen is 0
35 }
36 if (haylen < neelen) return nullptr;
37
38 const char* match;
39 const char* hayend = phaystack + haylen - neelen + 1;
40 // A static cast is used here to work around the fact that memchr returns
41 // a void* on Posix-compliant systems and const void* on Windows.
42 while (
43 (match = static_cast<const char*>(memchr(
44 phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) {
45 if (memcmp(match, pneedle, neelen) == 0)
46 return match;
47 else
48 phaystack = match + 1;
49 }
50 return nullptr;
51 }
52
WritePadding(std::ostream & o,size_t pad)53 void WritePadding(std::ostream& o, size_t pad) {
54 char fill_buf[32];
55 memset(fill_buf, o.fill(), sizeof(fill_buf));
56 while (pad) {
57 size_t n = std::min(pad, sizeof(fill_buf));
58 o.write(fill_buf, static_cast<std::streamsize>(n));
59 pad -= n;
60 }
61 }
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.
LookupTable(string_view wanted)69 explicit LookupTable(string_view wanted) {
70 for (char c : wanted) {
71 table_[Index(c)] = true;
72 }
73 }
operator [](char c) const74 bool operator[](char c) const { return table_[Index(c)]; }
75
76 private:
Index(char c)77 static unsigned char Index(char c) { return static_cast<unsigned char>(c); }
78 bool table_[UCHAR_MAX + 1] = {};
79 };
80
81 } // namespace
82
operator <<(std::ostream & o,string_view piece)83 std::ostream& operator<<(std::ostream& o, string_view piece) {
84 std::ostream::sentry sentry(o);
85 if (sentry) {
86 size_t lpad = 0;
87 size_t rpad = 0;
88 if (static_cast<size_t>(o.width()) > piece.size()) {
89 size_t pad = static_cast<size_t>(o.width()) - piece.size();
90 if ((o.flags() & o.adjustfield) == o.left) {
91 rpad = pad;
92 } else {
93 lpad = pad;
94 }
95 }
96 if (lpad) WritePadding(o, lpad);
97 o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
98 if (rpad) WritePadding(o, rpad);
99 o.width(0);
100 }
101 return o;
102 }
103
find(string_view s,size_type pos) const104 string_view::size_type string_view::find(string_view s,
105 size_type pos) const noexcept {
106 if (empty() || pos > length_) {
107 if (empty() && pos == 0 && s.empty()) return 0;
108 return npos;
109 }
110 const char* result = memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_);
111 return result ? static_cast<size_type>(result - ptr_) : npos;
112 }
113
find(char c,size_type pos) const114 string_view::size_type string_view::find(char c, size_type pos) const noexcept {
115 if (empty() || pos >= length_) {
116 return npos;
117 }
118 const char* result =
119 static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos));
120 return result != nullptr ? static_cast<size_type>(result - ptr_) : npos;
121 }
122
rfind(string_view s,size_type pos) const123 string_view::size_type string_view::rfind(string_view s,
124 size_type pos) const noexcept {
125 if (length_ < s.length_) return npos;
126 if (s.empty()) return std::min(length_, pos);
127 const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
128 const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
129 return result != last ? static_cast<size_type>(result - ptr_) : npos;
130 }
131
132 // Search range is [0..pos] inclusive. If pos == npos, search everything.
rfind(char c,size_type pos) const133 string_view::size_type string_view::rfind(char c,
134 size_type pos) const noexcept {
135 // Note: memrchr() is not available on Windows.
136 if (empty()) return npos;
137 for (size_type i = std::min(pos, length_ - 1);; --i) {
138 if (ptr_[i] == c) {
139 return i;
140 }
141 if (i == 0) break;
142 }
143 return npos;
144 }
145
find_first_of(string_view s,size_type pos) const146 string_view::size_type string_view::find_first_of(
147 string_view s, size_type pos) const noexcept {
148 if (empty() || s.empty()) {
149 return npos;
150 }
151 // Avoid the cost of LookupTable() for a single-character search.
152 if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
153 LookupTable tbl(s);
154 for (size_type i = pos; i < length_; ++i) {
155 if (tbl[ptr_[i]]) {
156 return i;
157 }
158 }
159 return npos;
160 }
161
find_first_not_of(string_view s,size_type pos) const162 string_view::size_type string_view::find_first_not_of(
163 string_view s, size_type pos) const noexcept {
164 if (empty()) return npos;
165 // Avoid the cost of LookupTable() for a single-character search.
166 if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
167 LookupTable tbl(s);
168 for (size_type i = pos; i < length_; ++i) {
169 if (!tbl[ptr_[i]]) {
170 return i;
171 }
172 }
173 return npos;
174 }
175
find_first_not_of(char c,size_type pos) const176 string_view::size_type string_view::find_first_not_of(
177 char c, size_type pos) const noexcept {
178 if (empty()) return npos;
179 for (; pos < length_; ++pos) {
180 if (ptr_[pos] != c) {
181 return pos;
182 }
183 }
184 return npos;
185 }
186
find_last_of(string_view s,size_type pos) const187 string_view::size_type string_view::find_last_of(string_view s,
188 size_type pos) const noexcept {
189 if (empty() || s.empty()) return npos;
190 // Avoid the cost of LookupTable() for a single-character search.
191 if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
192 LookupTable tbl(s);
193 for (size_type i = std::min(pos, length_ - 1);; --i) {
194 if (tbl[ptr_[i]]) {
195 return i;
196 }
197 if (i == 0) break;
198 }
199 return npos;
200 }
201
find_last_not_of(string_view s,size_type pos) const202 string_view::size_type string_view::find_last_not_of(
203 string_view s, size_type pos) const noexcept {
204 if (empty()) return npos;
205 size_type i = std::min(pos, length_ - 1);
206 if (s.empty()) return i;
207 // Avoid the cost of LookupTable() for a single-character search.
208 if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
209 LookupTable tbl(s);
210 for (;; --i) {
211 if (!tbl[ptr_[i]]) {
212 return i;
213 }
214 if (i == 0) break;
215 }
216 return npos;
217 }
218
find_last_not_of(char c,size_type pos) const219 string_view::size_type string_view::find_last_not_of(
220 char c, size_type pos) const noexcept {
221 if (empty()) return npos;
222 size_type i = std::min(pos, length_ - 1);
223 for (;; --i) {
224 if (ptr_[i] != c) {
225 return i;
226 }
227 if (i == 0) break;
228 }
229 return npos;
230 }
231
232
233 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
234 constexpr string_view::size_type string_view::npos;
235 constexpr string_view::size_type string_view::kMaxSize;
236 #endif
237
238 ABSL_NAMESPACE_END
239 } // namespace absl
240
241 #endif // ABSL_USES_STD_STRING_VIEW
242