string_view.cc (7708B)
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. 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 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. 73 explicit LookupTable(string_view wanted) { 74 for (char c : wanted) { 75 table_[Index(c)] = true; 76 } 77 } 78 bool operator[](char c) const { return table_[Index(c)]; } 79 80 private: 81 static unsigned char Index(char c) { return static_cast<unsigned char>(c); } 82 bool table_[UCHAR_MAX + 1] = {}; 83 }; 84 85 } // namespace 86 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 108 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 118 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 127 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. 137 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 150 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 166 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 180 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 191 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 206 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 223 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 ABSL_NAMESPACE_END 237 } // namespace absl 238 239 #else 240 241 // https://github.com/abseil/abseil-cpp/issues/1465 242 // CMake builds on Apple platforms error when libraries are empty. 243 // Our CMake configuration can avoid this error on header-only libraries, 244 // but since this library is conditionally empty, including a single 245 // variable is an easy workaround. 246 #ifdef __APPLE__ 247 namespace absl { 248 ABSL_NAMESPACE_BEGIN 249 namespace strings_internal { 250 extern const char kAvoidEmptyStringViewLibraryWarning; 251 const char kAvoidEmptyStringViewLibraryWarning = 0; 252 } // namespace strings_internal 253 ABSL_NAMESPACE_END 254 } // namespace absl 255 #endif // __APPLE__ 256 257 #endif // ABSL_USES_STD_STRING_VIEW