1 // Copyright 2015 The Shaderc Authors. All rights reserved. 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 // http://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 #ifndef LIBSHADERC_UTIL_STRING_PIECE_H_ 16 #define LIBSHADERC_UTIL_STRING_PIECE_H_ 17 18 #include <cassert> 19 #include <cstring> 20 #include <ostream> 21 #include <vector> 22 23 namespace shaderc_util { 24 // Provides a read-only view into a string (cstring or std::string). 25 // This must be created after the string in question, and cannot 26 // outlive the memory of the string in question. 27 // Any operations that may modify the location or size of the 28 // original data render the associated string_piece invalid. 29 30 class string_piece { 31 public: 32 typedef const char* iterator; 33 static const size_t npos = -1; 34 35 string_piece() {} 36 37 string_piece(const char* begin, const char* end) : begin_(begin), end_(end) { 38 assert((begin == nullptr) == (end == nullptr) && 39 "either both begin and end must be nullptr or neither must be"); 40 } 41 42 string_piece(const char* string) : begin_(string), end_(string) { 43 if (string) { 44 end_ += strlen(string); 45 } 46 } 47 48 string_piece(const std::string& str) { 49 if (!str.empty()) { 50 begin_ = &(str.front()); 51 end_ = &(str.back()) + 1; 52 } 53 } 54 55 string_piece(const string_piece& other) { 56 begin_ = other.begin_; 57 end_ = other.end_; 58 } 59 60 // Clears the string_piece removing any reference to the original string. 61 void clear() { 62 begin_ = nullptr; 63 end_ = nullptr; 64 } 65 66 // Returns a pointer to the data contained in the underlying string. 67 // If there is no underlying string, returns a nullptr. 68 const char* data() const { return begin_; } 69 70 // Returns an std::string copy of the internal data. 71 std::string str() const { return std::string(begin_, end_); } 72 73 // Returns a string_piece that points to a substring in the original string. 74 string_piece substr(size_t pos, size_t len = npos) const { 75 assert(len == npos || pos + len <= size()); 76 return string_piece(begin_ + pos, len == npos ? end_ : begin_ + pos + len); 77 } 78 79 // Takes any function object predicate that takes a char and returns a 80 // boolean. 81 // Returns the index of the first element that does not return true for the 82 // predicate. 83 // Returns string_piece::npos if all elements match. 84 template <typename T> 85 size_t find_first_not_matching(T callee) { 86 for (auto it = begin_; it != end_; ++it) { 87 if (!callee(*it)) { 88 return it - begin_; 89 } 90 } 91 return npos; 92 } 93 94 // Returns the index of the first character that does not match any character 95 // in the input string_piece. 96 // The search only includes characters at or after position pos. 97 // Returns string_piece::npos if all match. 98 size_t find_first_not_of(const string_piece& to_search, 99 size_t pos = 0) const { 100 if (pos >= size()) { 101 return npos; 102 } 103 for (auto it = begin_ + pos; it != end_; ++it) { 104 if (to_search.find_first_of(*it) == npos) { 105 return it - begin_; 106 } 107 } 108 return npos; 109 } 110 111 // Returns find_first_not_of(str, pos) where str is a string_piece 112 // containing only to_search. 113 size_t find_first_not_of(char to_search, size_t pos = 0) const { 114 return find_first_not_of(string_piece(&to_search, &to_search + 1), pos); 115 } 116 117 // Returns the index of the first character that matches any character in the 118 // input string_piece. 119 // The search only includes characters at or after position pos. 120 // Returns string_piece::npos if there is no match. 121 size_t find_first_of(const string_piece& to_search, size_t pos = 0) const { 122 if (pos >= size()) { 123 return npos; 124 } 125 for (auto it = begin_ + pos; it != end_; ++it) { 126 for (char c : to_search) { 127 if (c == *it) { 128 return it - begin_; 129 } 130 } 131 } 132 return npos; 133 } 134 135 // Returns find_first_of(str, pos) where str is a string_piece 136 // containing only to_search. 137 size_t find_first_of(char to_search, size_t pos = 0) const { 138 return find_first_of(string_piece(&to_search, &to_search + 1), pos); 139 } 140 141 // Returns the index of the last character that matches any character in the 142 // input string_piece. 143 // The search only includes characters at or before position pos. 144 // Returns string_piece::npos if there is no match. 145 size_t find_last_of(const string_piece& to_search, size_t pos = npos) const { 146 if (empty()) return npos; 147 if (pos >= size()) { 148 pos = size(); 149 } 150 auto it = begin_ + pos + 1; 151 do { 152 --it; 153 if (to_search.find_first_of(*it) != npos) { 154 return it - begin_; 155 } 156 } while (it != begin_); 157 return npos; 158 } 159 160 // Returns find_last_of(str, pos) where str is a string_piece 161 // containing only to_search. 162 size_t find_last_of(char to_search, size_t pos = npos) const { 163 return find_last_of(string_piece(&to_search, &to_search + 1), pos); 164 } 165 166 // Returns the index of the last character that does not match any character 167 // in the input string_piece. 168 // The search only includes characters at or before position pos. 169 // Returns string_piece::npos if there is no match. 170 size_t find_last_not_of(const string_piece& to_search, 171 size_t pos = npos) const { 172 if (empty()) return npos; 173 if (pos >= size()) { 174 pos = size(); 175 } 176 auto it = begin_ + pos + 1; 177 do { 178 --it; 179 if (to_search.find_first_of(*it) == npos) { 180 return it - begin_; 181 } 182 } while (it != begin_); 183 return npos; 184 } 185 186 // Returns find_last_not_of(str, pos) where str is a string_piece 187 // containing only to_search. 188 size_t find_last_not_of(char to_search, size_t pos = 0) const { 189 return find_last_not_of(string_piece(&to_search, &to_search + 1), pos); 190 } 191 192 // Continuously removes characters appearing in chars_to_strip from the left. 193 string_piece lstrip(const string_piece& chars_to_strip) const { 194 iterator begin = begin_; 195 for (; begin < end_; ++begin) 196 if (chars_to_strip.find_first_of(*begin) == npos) break; 197 if (begin >= end_) return string_piece(); 198 return string_piece(begin, end_); 199 } 200 201 // Continuously removes characters appearing in chars_to_strip from the right. 202 string_piece rstrip(const string_piece& chars_to_strip) const { 203 iterator end = end_; 204 for (; begin_ < end; --end) 205 if (chars_to_strip.find_first_of(*(end - 1)) == npos) break; 206 if (begin_ >= end) return string_piece(); 207 return string_piece(begin_, end); 208 } 209 210 // Continuously removes characters appearing in chars_to_strip from both 211 // sides. 212 string_piece strip(const string_piece& chars_to_strip) const { 213 return lstrip(chars_to_strip).rstrip(chars_to_strip); 214 } 215 216 string_piece strip_whitespace() const { return strip(" \t\n\r\f\v"); } 217 218 // Returns the character at index i in the string_piece. 219 const char& operator[](size_t i) const { return *(begin_ + i); } 220 221 // Standard comparison operator. 222 bool operator==(const string_piece& other) const { 223 // Either end_ and _begin_ are nullptr or neither of them are. 224 assert(((end_ == nullptr) == (begin_ == nullptr))); 225 assert(((other.end_ == nullptr) == (other.begin_ == nullptr))); 226 if (size() != other.size()) { 227 return false; 228 } 229 return (memcmp(begin_, other.begin_, end_ - begin_) == 0); 230 } 231 232 bool operator!=(const string_piece& other) const { 233 return !operator==(other); 234 } 235 236 // Returns an iterator to the first element. 237 iterator begin() const { return begin_; } 238 239 // Returns an iterator to one past the last element. 240 iterator end() const { return end_; } 241 242 const char& front() const { 243 assert(!empty()); 244 return *begin_; 245 } 246 247 const char& back() const { 248 assert(!empty()); 249 return *(end_ - 1); 250 } 251 252 // Returns true is this string_piece starts with the same 253 // characters as other. 254 bool starts_with(const string_piece& other) const { 255 const char* iter = begin_; 256 const char* other_iter = other.begin(); 257 while (iter != end_ && other_iter != other.end()) { 258 if (*iter++ != *other_iter++) { 259 return false; 260 } 261 } 262 return other_iter == other.end(); 263 } 264 265 // Returns the index of the start of the first substring that matches 266 // the input string_piece. 267 // The search only includes substrings starting at or after position pos. 268 // Returns npos if the string cannot be found. 269 size_t find(const string_piece& substr, size_t pos = 0) const { 270 if (empty()) return npos; 271 if (pos >= size()) return npos; 272 if (substr.empty()) return 0; 273 for (auto it = begin_ + pos; 274 end() - it >= static_cast<decltype(end() - it)>(substr.size()); ++it) { 275 if (string_piece(it, end()).starts_with(substr)) return it - begin_; 276 } 277 return npos; 278 } 279 280 // Returns the index of the start of the first character that matches 281 // the input character. 282 // The search only includes substrings starting at or after position pos. 283 // Returns npos if the character cannot be found. 284 size_t find(char character, size_t pos = 0) const { 285 return find_first_of(character, pos); 286 } 287 288 // Returns true if the string_piece is empty. 289 bool empty() const { return begin_ == end_; } 290 291 // Returns the number of characters in the string_piece. 292 size_t size() const { return end_ - begin_; } 293 294 // Returns a vector of string_pieces representing delimiter delimited 295 // fields found. If the keep_delimiter parameter is true, then each 296 // delimiter character is kept with the string to its left. 297 std::vector<string_piece> get_fields(char delimiter, 298 bool keep_delimiter = false) const { 299 std::vector<string_piece> fields; 300 size_t first = 0; 301 size_t field_break = find_first_of(delimiter); 302 while (field_break != npos) { 303 fields.push_back(substr(first, field_break - first + keep_delimiter)); 304 first = field_break + 1; 305 field_break = find_first_of(delimiter, first); 306 } 307 if (size() - first > 0) { 308 fields.push_back(substr(first, size() - first)); 309 } 310 return fields; 311 } 312 313 friend std::ostream& operator<<(std::ostream& os, const string_piece& piece); 314 315 private: 316 // It is expected that begin_ and end_ will both be null or 317 // they will both point to valid pieces of memory, but it is invalid 318 // to have one of them being nullptr and the other not. 319 string_piece::iterator begin_ = nullptr; 320 string_piece::iterator end_ = nullptr; 321 }; 322 323 inline std::ostream& operator<<(std::ostream& os, const string_piece& piece) { 324 // Either end_ and _begin_ are nullptr or neither of them are. 325 assert(((piece.end_ == nullptr) == (piece.begin_ == nullptr))); 326 if (piece.end_ != piece.begin_) { 327 os.write(piece.begin_, piece.end_ - piece.begin_); 328 } 329 return os; 330 } 331 332 inline bool operator==(const char* first, const string_piece second) { 333 return second == first; 334 } 335 336 inline bool operator!=(const char* first, const string_piece second) { 337 return !operator==(first, second); 338 } 339 } 340 341 namespace std { 342 template <> 343 struct hash<shaderc_util::string_piece> { 344 size_t operator()(const shaderc_util::string_piece& piece) const { 345 // djb2 algorithm. 346 size_t hash = 5381; 347 for (char c : piece) { 348 hash = ((hash << 5) + hash) + c; 349 } 350 return hash; 351 } 352 }; 353 } 354 355 #endif // LIBSHADERC_UTIL_STRING_PIECE_H_ 356