string_view.cc 7.7 KB

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