pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Pavel Kalinnikov | d797063 | 2017-06-20 09:07:34 | [diff] [blame] | 5 | #ifndef COMPONENTS_URL_PATTERN_INDEX_STRING_SPLITTER_H_ |
| 6 | #define COMPONENTS_URL_PATTERN_INDEX_STRING_SPLITTER_H_ |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 7 | |
| 8 | #include <iterator> |
| 9 | |
| 10 | #include "base/logging.h" |
| 11 | #include "base/strings/string_piece.h" |
| 12 | |
Pavel Kalinnikov | d797063 | 2017-06-20 09:07:34 | [diff] [blame] | 13 | namespace url_pattern_index { |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 14 | |
| 15 | // A zero-allocation string splitter. Splits a string into non-empty tokens |
| 16 | // divided by separator characters as defined by the IsSeparator predicate. |
| 17 | // However, instead of materializing and returning a collection of all tokens in |
| 18 | // the string, it provides an InputIterator that can be used to extract the |
| 19 | // tokens. |
| 20 | // |
| 21 | // TODO(pkalinnikov): Move it to "base/strings" after some generalization. |
| 22 | template <typename IsSeparator> |
| 23 | class StringSplitter { |
| 24 | public: |
| 25 | class Iterator |
| 26 | : public std::iterator<std::input_iterator_tag, base::StringPiece> { |
| 27 | public: |
| 28 | // Creates an iterator, which points to the leftmost token within the |
| 29 | // |splitter|'s |text|, starting from |head|. |
| 30 | Iterator(const StringSplitter& splitter, |
| 31 | base::StringPiece::const_iterator head) |
pkalinnikov | 854818d6 | 2016-07-22 11:55:10 | [diff] [blame] | 32 | : splitter_(&splitter), current_(head, 0), end_(splitter.text_.end()) { |
| 33 | DCHECK_GE(head, splitter_->text_.begin()); |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 34 | DCHECK_LE(head, end_); |
| 35 | |
| 36 | Advance(); |
| 37 | } |
| 38 | |
| 39 | bool operator==(const Iterator& rhs) const { |
| 40 | return current_.begin() == rhs.current_.begin(); |
| 41 | } |
| 42 | |
| 43 | bool operator!=(const Iterator& rhs) const { return !operator==(rhs); } |
| 44 | |
| 45 | base::StringPiece operator*() const { return current_; } |
| 46 | const base::StringPiece* operator->() const { return ¤t_; } |
| 47 | |
| 48 | Iterator& operator++() { |
| 49 | Advance(); |
| 50 | return *this; |
| 51 | } |
| 52 | |
| 53 | Iterator operator++(int) { |
| 54 | Iterator copy(*this); |
| 55 | operator++(); |
| 56 | return copy; |
| 57 | } |
| 58 | |
| 59 | private: |
| 60 | void Advance() { |
| 61 | auto begin = current_.end(); |
pkalinnikov | 854818d6 | 2016-07-22 11:55:10 | [diff] [blame] | 62 | while (begin != end_ && splitter_->is_separator_(*begin)) |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 63 | ++begin; |
| 64 | auto end = begin; |
pkalinnikov | 854818d6 | 2016-07-22 11:55:10 | [diff] [blame] | 65 | while (end != end_ && !splitter_->is_separator_(*end)) |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 66 | ++end; |
| 67 | current_ = base::StringPiece(begin, end - begin); |
| 68 | } |
| 69 | |
pkalinnikov | 854818d6 | 2016-07-22 11:55:10 | [diff] [blame] | 70 | const StringSplitter* splitter_; |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 71 | |
| 72 | // Contains the token currently pointed to by the iterator. |
| 73 | base::StringPiece current_; |
| 74 | // Always points to the text_.end(). |
| 75 | base::StringPiece::const_iterator end_; |
| 76 | }; |
| 77 | |
| 78 | // Constructs a splitter for iterating over non-empty tokens contained in the |
| 79 | // |text|. |is_separator| predicate is used to determine whether a certain |
| 80 | // character is a separator. |
| 81 | StringSplitter(base::StringPiece text, |
| 82 | IsSeparator is_separator = IsSeparator()) |
| 83 | : text_(text), is_separator_(is_separator) {} |
| 84 | |
| 85 | Iterator begin() const { return Iterator(*this, text_.begin()); } |
| 86 | Iterator end() const { return Iterator(*this, text_.end()); } |
| 87 | |
| 88 | private: |
| 89 | base::StringPiece text_; |
| 90 | IsSeparator is_separator_; |
| 91 | }; |
| 92 | |
| 93 | template <typename IsSeparator> |
| 94 | StringSplitter<IsSeparator> CreateStringSplitter(base::StringPiece text, |
| 95 | IsSeparator is_separator) { |
| 96 | return StringSplitter<IsSeparator>(text, is_separator); |
| 97 | } |
| 98 | |
Pavel Kalinnikov | d797063 | 2017-06-20 09:07:34 | [diff] [blame] | 99 | } // namespace url_pattern_index |
pkalinnikov | e77a62e | 2016-06-24 10:21:40 | [diff] [blame] | 100 | |
Pavel Kalinnikov | d797063 | 2017-06-20 09:07:34 | [diff] [blame] | 101 | #endif // COMPONENTS_URL_PATTERN_INDEX_STRING_SPLITTER_H_ |