1 /*
2  *  Copyright (c) 2016, Facebook, Inc.
3  *  All rights reserved.
4  *
5  *  This source code is licensed under the BSD-style license found in the
6  *  LICENSE file in the root directory of this source tree. An additional grant
7  *  of patent rights can be found in the PATENTS file in the same directory.
8  */
9 
10 #ifndef FATAL_INCLUDE_fatal_string_tokenizer_h
11 #define FATAL_INCLUDE_fatal_string_tokenizer_h
12 
13 #include <fatal/container/uninitialized.h>
14 #include <fatal/string/string_view.h>
15 #include <fatal/type/traits.h>
16 
17 #include <type_traits>
18 #include <utility>
19 
20 namespace fatal {
21 
22 template <typename Token, char Delimiter>
23 struct tokenizer {
24   using token = Token;
25   using delimiter = std::integral_constant<char, Delimiter>;
26 
27   template <typename... Args, typename = safe_overload<tokenizer, Args...>>
tokenizertokenizer28   explicit tokenizer(Args &&...args): data_(std::forward<Args>(args)...) {}
29 
30   struct const_iterator {
const_iteratortokenizer::const_iterator31     const_iterator(const_iterator const &rhs):
32       data_(rhs.data_)
33     {
34       token_.construct(*rhs.token_);
35     }
36 
const_iteratortokenizer::const_iterator37     const_iterator(const_iterator &&rhs):
38       data_(std::move(rhs.data_))
39     {
40       token_.construct(std::move(*rhs.token_));
41     }
42 
const_iteratortokenizer::const_iterator43     explicit const_iterator(string_view data):
44       data_(data)
45     {
46       token_.construct(data_.seek_past(delimiter::value));
47     }
48 
49     const_iterator &operator ++() {
50       token_.destroy();
51       token_.construct(data_.seek_past(delimiter::value));
52       return *this;
53     }
54 
55     const_iterator &operator ++(int) {
56       auto copy(*this);
57       ++*this;
58       return copy;
59     }
60 
61     token const *operator ->() const { return token_.ptr(); }
62     token const &operator *() const { return *token_; }
63 
64     bool operator ==(const_iterator const &rhs) const {
65       return data_.begin() == rhs.data_.begin()
66         && data_.end() == rhs.data_.end()
67         && *token_ == *rhs.token_;
68     }
69 
70     bool operator !=(const_iterator const &rhs) const {
71       return !(*this == rhs);
72     }
73 
74   private:
75     string_view data_;
76     uninitialized<token, true> token_;
77   };
78 
cbegintokenizer79   const_iterator cbegin() const { return const_iterator(data_); }
80 
begintokenizer81   const_iterator begin() const { return cbegin(); }
82 
cendtokenizer83   const_iterator cend() const {
84     return const_iterator(string_view(data_.end(), data_.end()));
85   }
86 
endtokenizer87   const_iterator end() const { return cend(); }
88 
emptytokenizer89   bool empty() const { return !data_; }
90 
91   bool operator ==(tokenizer const &rhs) const {
92     return data_.begin() == rhs.data_.begin()
93       && data_.end() == rhs.data_.end();
94   }
95 
96   bool operator !=(tokenizer const &rhs) const { return !(*this == rhs); }
97 
98 private:
99   string_view data_;
100 };
101 
102 
103 using colon_tokenizer = tokenizer<string_view, ':'>;
104 using comma_tokenizer = tokenizer<string_view, ','>;
105 using line_tokenizer = tokenizer<string_view, '\n'>;
106 using semicolon_tokenizer = tokenizer<string_view, ';'>;
107 using space_tokenizer = tokenizer<string_view, ' '>;
108 
109 using csv_tokenizer = tokenizer<comma_tokenizer, '\n'>;
110 
111 } // namespace fatal {
112 
113 #endif // FATAL_INCLUDE_fatal_string_tokenizer_h
114