Promoting out of experimental
[folly.git] / folly / gen / String-inl.h
1 /*
2  * Copyright 2014 Facebook, Inc.
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 FOLLY_GEN_STRING_H
18 #error This file may only be included from folly/gen/String.h
19 #endif
20
21 #include "folly/Conv.h"
22 #include "folly/String.h"
23 #include "folly/io/IOBuf.h"
24
25 namespace folly {
26 namespace gen {
27 namespace detail {
28
29 inline bool splitPrefix(StringPiece& in, StringPiece& prefix, char delimiter) {
30   auto p = static_cast<const char*>(memchr(in.data(), delimiter, in.size()));
31   if (p) {
32     prefix.assign(in.data(), p);
33     in.assign(p + 1, in.end());
34     return true;
35   }
36   prefix.clear();
37   return false;
38 }
39
40 inline const char* ch(const unsigned char* p) {
41   return reinterpret_cast<const char*>(p);
42 }
43
44 class StringResplitter : public Operator<StringResplitter> {
45   char delimiter_;
46  public:
47   explicit StringResplitter(char delimiter) : delimiter_(delimiter) { }
48
49   template <class Source>
50   class Generator : public GenImpl<StringPiece, Generator<Source>> {
51     Source source_;
52     char delimiter_;
53    public:
54     Generator(Source source, char delimiter)
55       : source_(std::move(source)), delimiter_(delimiter) { }
56
57     template <class Body>
58     bool apply(Body&& body) const {
59       std::unique_ptr<IOBuf> buffer;
60
61       auto fn = [&](StringPiece in) -> bool {
62         StringPiece prefix;
63         bool found = splitPrefix(in, prefix, this->delimiter_);
64         if (found && buffer && buffer->length() != 0) {
65           // Append to end of buffer, return line
66           if (!prefix.empty()) {
67             buffer->reserve(0, prefix.size());
68             memcpy(buffer->writableTail(), prefix.data(), prefix.size());
69             buffer->append(prefix.size());
70           }
71           if (!body(StringPiece(ch(buffer->data()), buffer->length()))) {
72             return false;
73           }
74           buffer->clear();
75           found = splitPrefix(in, prefix, this->delimiter_);
76         }
77         // Buffer is empty, return lines directly from input (no buffer)
78         while (found) {
79           if (!body(prefix)) {
80             return false;
81           }
82           found = splitPrefix(in, prefix, this->delimiter_);
83         }
84         if (!in.empty()) {
85           // Incomplete line left, append to buffer
86           if (!buffer) {
87             // Arbitrarily assume that we have half a line and get enough
88             // room for twice that.
89             constexpr size_t kDefaultLineSize = 256;
90             buffer = IOBuf::create(std::max(kDefaultLineSize, 2 * in.size()));
91           }
92           buffer->reserve(0, in.size());
93           memcpy(buffer->writableTail(), in.data(), in.size());
94           buffer->append(in.size());
95         }
96         return true;
97       };
98
99       // Iterate
100       if (!source_.apply(std::move(fn))) {
101         return false;
102       }
103
104       // Incomplete last line
105       if (buffer && buffer->length() != 0) {
106         if (!body(StringPiece(ch(buffer->data()), buffer->length()))) {
107           return false;
108         }
109       }
110       return true;
111     }
112
113     static constexpr bool infinite = Source::infinite;
114   };
115
116   template<class Source,
117            class Value,
118            class Gen = Generator<Source>>
119   Gen compose(GenImpl<Value, Source>&& source) const {
120     return Gen(std::move(source.self()), delimiter_);
121   }
122
123   template<class Source,
124            class Value,
125            class Gen = Generator<Source>>
126   Gen compose(const GenImpl<Value, Source>& source) const {
127     return Gen(source.self(), delimiter_);
128   }
129 };
130
131 class SplitStringSource : public GenImpl<StringPiece, SplitStringSource> {
132   StringPiece source_;
133   char delimiter_;
134  public:
135   SplitStringSource(const StringPiece& source,
136                     char delimiter)
137     : source_(source)
138     , delimiter_(delimiter) { }
139
140   template <class Body>
141   bool apply(Body&& body) const {
142     StringPiece rest(source_);
143     StringPiece prefix;
144     while (splitPrefix(rest, prefix, this->delimiter_)) {
145       if (!body(prefix)) {
146         return false;
147       }
148     }
149     if (!rest.empty()) {
150       if (!body(rest)) {
151         return false;
152       }
153     }
154     return true;
155   }
156 };
157
158 /**
159  * Unsplit - For joining tokens from a generator into a string.  This is
160  * the inverse of `split` above.
161  *
162  * This type is primarily used through the 'unsplit' function.
163  */
164 template<class Delimiter,
165          class Output>
166 class Unsplit : public Operator<Unsplit<Delimiter, Output>> {
167   Delimiter delimiter_;
168  public:
169   Unsplit(const Delimiter& delimiter)
170     : delimiter_(delimiter) {
171   }
172
173   template<class Source,
174            class Value>
175   Output compose(const GenImpl<Value, Source>& source) const {
176     Output outputBuffer;
177     UnsplitBuffer<Delimiter, Output> unsplitter(delimiter_, &outputBuffer);
178     unsplitter.compose(source);
179     return outputBuffer;
180   }
181 };
182
183 /**
184  * UnsplitBuffer - For joining tokens from a generator into a string,
185  * and inserting them into a custom buffer.
186  *
187  * This type is primarily used through the 'unsplit' function.
188  */
189 template<class Delimiter,
190          class OutputBuffer>
191 class UnsplitBuffer : public Operator<UnsplitBuffer<Delimiter, OutputBuffer>> {
192   Delimiter delimiter_;
193   OutputBuffer* outputBuffer_;
194  public:
195   UnsplitBuffer(const Delimiter& delimiter, OutputBuffer* outputBuffer)
196     : delimiter_(delimiter)
197     , outputBuffer_(outputBuffer) {
198     CHECK(outputBuffer);
199   }
200
201   template<class Source,
202            class Value>
203   void compose(const GenImpl<Value, Source>& source) const {
204     // If the output buffer is empty, we skip inserting the delimiter for the
205     // first element.
206     bool skipDelim = outputBuffer_->empty();
207     source | [&](Value v) {
208       if (skipDelim) {
209         skipDelim = false;
210         toAppend(std::forward<Value>(v), outputBuffer_);
211       } else {
212         toAppend(delimiter_, std::forward<Value>(v), outputBuffer_);
213       }
214     };
215   }
216 };
217
218
219 /**
220  * Hack for static for-like constructs
221  */
222 template<class Target, class=void>
223 inline Target passthrough(Target target) { return target; }
224
225 #pragma GCC diagnostic push
226 #ifdef __clang__
227 // Clang isn't happy with eatField() hack below.
228 #pragma GCC diagnostic ignored "-Wreturn-stack-address"
229 #endif  // __clang__
230
231 /**
232  * ParseToTuple - For splitting a record and immediatlely converting it to a
233  * target tuple type. Primary used through the 'eachToTuple' helper, like so:
234  *
235  *  auto config
236  *    = split("1:a 2:b", ' ')
237  *    | eachToTuple<int, string>()
238  *    | as<vector<tuple<int, string>>>();
239  *
240  */
241 template<class TargetContainer,
242          class Delimiter,
243          class... Targets>
244 class SplitTo {
245   Delimiter delimiter_;
246  public:
247   explicit SplitTo(Delimiter delimiter)
248     : delimiter_(delimiter) {}
249
250   TargetContainer operator()(StringPiece line) const {
251     int i = 0;
252     StringPiece fields[sizeof...(Targets)];
253     // HACK(tjackson): Used for referencing fields[] corresponding to variadic
254     // template parameters.
255     auto eatField = [&]() -> StringPiece& { return fields[i++]; };
256     if (!split(delimiter_,
257                line,
258                detail::passthrough<StringPiece&, Targets>(eatField())...)) {
259       throw std::runtime_error("field count mismatch");
260     }
261     i = 0;
262     return TargetContainer(To<Targets>()(eatField())...);
263   }
264 };
265
266 #pragma GCC diagnostic pop
267
268 }  // namespace detail
269
270 }  // namespace gen
271 }  // namespace folly