Home | History | Annotate | Download | only in libshaderc_util
      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