1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_ 18 #define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_ 19 20 #include <string.h> 21 22 #include <algorithm> 23 #include <string> 24 25 #include "perfetto/base/build_config.h" 26 #include "perfetto/base/logging.h" 27 #include "perfetto/ext/base/hash.h" 28 29 namespace perfetto { 30 namespace base { 31 32 // A string-like object that refers to a non-owned piece of memory. 33 // Strings are internally NOT null terminated. 34 class StringView { 35 public: 36 // Allow hashing with base::Hash. 37 static constexpr bool kHashable = true; 38 static constexpr size_t npos = static_cast<size_t>(-1); 39 StringView()40 StringView() : data_(nullptr), size_(0) {} 41 StringView(const StringView&) = default; 42 StringView& operator=(const StringView&) = default; StringView(const char * data,size_t size)43 StringView(const char* data, size_t size) : data_(data), size_(size) { 44 PERFETTO_DCHECK(size == 0 || data != nullptr); 45 } 46 47 // Allow implicit conversion from any class that has a |data| and |size| field 48 // and has the kConvertibleToStringView trait (e.g., protozero::ConstChars). 49 template <typename T, typename = std::enable_if<T::kConvertibleToStringView>> StringView(const T & x)50 StringView(const T& x) : StringView(x.data, x.size) { 51 PERFETTO_DCHECK(x.size == 0 || x.data != nullptr); 52 } 53 54 // Creates a StringView from a null-terminated C string. 55 // Deliberately not "explicit". StringView(const char * cstr)56 StringView(const char* cstr) : data_(cstr), size_(strlen(cstr)) { 57 PERFETTO_DCHECK(cstr != nullptr); 58 } 59 60 // This instead has to be explicit, as creating a StringView out of a 61 // std::string can be subtle. StringView(const std::string & str)62 explicit StringView(const std::string& str) 63 : data_(str.data()), size_(str.size()) {} 64 empty()65 bool empty() const { return size_ == 0; } size()66 size_t size() const { return size_; } data()67 const char* data() const { return data_; } begin()68 const char* begin() const { return data_; } end()69 const char* end() const { return data_ + size_; } 70 at(size_t pos)71 char at(size_t pos) const { 72 PERFETTO_DCHECK(pos < size_); 73 return data_[pos]; 74 } 75 76 size_t find(char c, size_t start_pos = 0) const { 77 for (size_t i = start_pos; i < size_; ++i) { 78 if (data_[i] == c) 79 return i; 80 } 81 return npos; 82 } 83 84 size_t find(const StringView& str, size_t start_pos = 0) const { 85 if (start_pos > size()) 86 return npos; 87 auto it = std::search(begin() + start_pos, end(), str.begin(), str.end()); 88 size_t pos = static_cast<size_t>(it - begin()); 89 return pos + str.size() <= size() ? pos : npos; 90 } 91 92 size_t find(const char* str, size_t start_pos = 0) const { 93 return find(StringView(str), start_pos); 94 } 95 rfind(char c)96 size_t rfind(char c) const { 97 for (size_t i = size_; i > 0; --i) { 98 if (data_[i - 1] == c) 99 return i - 1; 100 } 101 return npos; 102 } 103 104 StringView substr(size_t pos, size_t count = npos) const { 105 if (pos >= size_) 106 return StringView("", 0); 107 size_t rcount = std::min(count, size_ - pos); 108 return StringView(data_ + pos, rcount); 109 } 110 CaseInsensitiveEq(const StringView & other)111 bool CaseInsensitiveEq(const StringView& other) const { 112 if (size() != other.size()) 113 return false; 114 if (size() == 0) 115 return true; 116 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) 117 return _strnicmp(data(), other.data(), size()) == 0; 118 #else 119 return strncasecmp(data(), other.data(), size()) == 0; 120 #endif 121 } 122 StartsWith(const StringView & other)123 bool StartsWith(const StringView& other) const { 124 if (other.size() == 0) 125 return true; 126 if (size() == 0) 127 return false; 128 if (other.size() > size()) 129 return false; 130 return memcmp(data(), other.data(), other.size()) == 0; 131 } 132 EndsWith(const StringView & other)133 bool EndsWith(const StringView& other) const { 134 if (other.size() == 0) 135 return true; 136 if (size() == 0) 137 return false; 138 if (other.size() > size()) 139 return false; 140 size_t off = size() - other.size(); 141 return memcmp(data() + off, other.data(), other.size()) == 0; 142 } 143 ToStdString()144 std::string ToStdString() const { 145 return size_ == 0 ? "" : std::string(data_, size_); 146 } 147 Hash()148 uint64_t Hash() const { 149 base::Hasher hasher; 150 hasher.Update(data_, size_); 151 return hasher.digest(); 152 } 153 154 private: 155 const char* data_ = nullptr; 156 size_t size_ = 0; 157 }; 158 159 inline bool operator==(const StringView& x, const StringView& y) { 160 if (x.size() != y.size()) 161 return false; 162 if (x.size() == 0) 163 return true; 164 return memcmp(x.data(), y.data(), x.size()) == 0; 165 } 166 167 inline bool operator!=(const StringView& x, const StringView& y) { 168 return !(x == y); 169 } 170 171 inline bool operator<(const StringView& x, const StringView& y) { 172 auto size = std::min(x.size(), y.size()); 173 if (size == 0) 174 return x.size() < y.size(); 175 int result = memcmp(x.data(), y.data(), size); 176 return result < 0 || (result == 0 && x.size() < y.size()); 177 } 178 179 inline bool operator>=(const StringView& x, const StringView& y) { 180 return !(x < y); 181 } 182 183 inline bool operator>(const StringView& x, const StringView& y) { 184 return y < x; 185 } 186 187 inline bool operator<=(const StringView& x, const StringView& y) { 188 return !(y < x); 189 } 190 191 } // namespace base 192 } // namespace perfetto 193 194 template <> 195 struct std::hash<::perfetto::base::StringView> { 196 size_t operator()(const ::perfetto::base::StringView& sv) const { 197 return static_cast<size_t>(sv.Hash()); 198 } 199 }; 200 201 #endif // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_ 202