staticlib_io  1.2.11
 All Classes Namespaces Files Functions Variables
replacer_source.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016, alex at staticlibs.net
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 /*
18  * File: replacer_source.hpp
19  * Author: alex
20  *
21  * Created on November 5, 2016, 8:10 PM
22  */
23 
24 #ifndef STATICLIB_IO_REPLACER_SOURCE_HPP
25 #define STATICLIB_IO_REPLACER_SOURCE_HPP
26 
27 #include <cstring>
28 #include <functional>
29 #include <ios>
30 #include <map>
31 #include <string>
32 #include <vector>
33 
34 #include "staticlib/config.hpp"
35 
39 #include "staticlib/io/span.hpp"
40 
41 namespace staticlib {
42 namespace io {
43 
47 template <typename Source>
49  // Replace state class
50  enum class State {
51  PREFIX, PLACEHOLDER, MOVED_FROM
52  };
53 
61  bool exhausted = false;
65  std::map<std::string, std::string> values;
69  std::function<void(const std::string&) > on_error;
73  std::string prefix;
77  std::string postfix;
81  size_t max_placeholder_len;
82 
86  std::vector<char> buffer;
87  size_t buf_len_limit = 4096;
88  size_t pos = 0;
89  std::string placeholder;
90  size_t ind_prefix = 0;
91  size_t ind_postfix = 0;
92  State state = State::PREFIX;
93 
94 public:
95 
102  replacer_source(Source&& src, std::map<std::string, std::string> values,
103  std::function<void(const std::string&)> on_error,
104  std::string prefix = "{{", std::string postfix = "}}", size_t max_placeholder_len = 255) :
105  src(make_buffered_source(std::move(src))),
106  values(std::move(values)),
107  on_error(on_error),
108  prefix(std::move(prefix)),
109  postfix(std::move(postfix)),
110  max_placeholder_len(max_placeholder_len) { }
111 
117  replacer_source(const replacer_source&) = delete;
118 
125  replacer_source& operator=(const replacer_source&) = delete;
126 
132  replacer_source(replacer_source&& other) STATICLIB_NOEXCEPT :
133  src(std::move(other.src)),
134  exhausted(other.exhausted),
135  values(std::move(other.values)),
136  on_error(std::move(other.on_error)),
137  prefix(std::move(other.prefix)),
138  postfix(std::move(other.postfix)),
139  max_placeholder_len(other.max_placeholder_len),
140  buffer(std::move(other.buffer)),
141  buf_len_limit(other.buf_len_limit),
142  pos(other.pos),
143  placeholder(std::move(other.placeholder)),
144  ind_prefix(other.ind_prefix),
145  ind_postfix(other.ind_postfix),
146  state(other.state) {
147  other.exhausted = true;
148  other.max_placeholder_len = 0;
149  other.buf_len_limit = 0;
150  other.pos = 0;
151  other.ind_prefix = 0;
152  other.ind_postfix = 0;
153  other.state = State::MOVED_FROM;
154  }
155 
162  replacer_source& operator=(replacer_source&& other) STATICLIB_NOEXCEPT {
163  src = std::move(other.src);
164  exhausted = other.exhausted;
165  other.exhausted = true;
166  values = std::move(other.values);
167  on_error = std::move(other.on_error);
168  prefix = std::move(other.prefix);
169  postfix = std::move(other.postfix);
170  max_placeholder_len = other.max_placeholder_len;
171  other.max_placeholder_len = 0;
172  buffer = std::move(other.buffer);
173  buf_len_limit = other.buf_len_limit;
174  other.buf_len_limit = 0;
175  pos = other.pos;
176  other.pos = 0;
177  placeholder = std::move(other.placeholder);
178  ind_prefix = other.ind_prefix;
179  other.ind_prefix = 0;
180  ind_postfix = other.ind_postfix;
181  other.ind_postfix = 0;
182  state = other.state;
183  other.state = State::MOVED_FROM;
184  return *this;
185  }
186 
193  std::streamsize read(span<char> span) {
194  size_t ulen = span.size();
195  // return from buffer
196  std::streamsize read_buffered = copy_buffered(span.data(), ulen);
197  if (read_buffered > 0) {
198  return read_buffered;
199  }
200  // fill buffer
201  if (!exhausted) {
202  buffer.resize(0);
203  pos = 0;
204  size_t limitlen = ulen <= buf_len_limit ? ulen : buf_len_limit;
205  for(;;) {
206  char cur;
207  std::streamsize read = 0;
208  while (0 == (read = src.read({std::addressof(cur), 1})));
209  exhausted = std::char_traits<char>::eof() == read;
210  if (exhausted) {
211  break;
212  }
213  switch (state) {
214  case State::PREFIX:
215  do_prefix(cur);
216  break;
217  case State::PLACEHOLDER:
218  do_placeholder(cur);
219  break;
220  case State::MOVED_FROM:
221  throw io_exception(TRACEMSG("Invalid attempt to read from source in 'MOVED_FROM' state"));
222  }
223  if (buffer.size() >= limitlen) {
224  break;
225  }
226  }
227  }
228  // return if any
229  std::streamsize read_prepared = copy_buffered(span.data(), ulen);
230  if (read_prepared > 0) {
231  return read_prepared;
232  }
233  // return eof
234  if (placeholder.length() > 0) {
235  std::string msg = "Invalid unclosed placeholder: [" + placeholder + "]";
236  on_error(msg);
237  }
238  return std::char_traits<char>::eof();
239  }
240 
246  Source& get_source() {
247  return src.get_source();
248  }
249 
255  std::map<std::string, std::string>& get_values() {
256  return values;
257  }
258 
259 private:
260 
261  void do_prefix(char cur) {
262  if (cur != prefix[ind_prefix]) {
263  if (ind_prefix > 0) {
264  size_t wp = buffer.size();
265  buffer.resize(wp + ind_prefix);
266  std::memcpy(buffer.data() + wp, prefix.c_str(), ind_prefix);
267  ind_prefix = 0;
268  }
269  buffer.push_back(cur);
270  } else {
271  ind_prefix += 1;
272  if (prefix.length() == ind_prefix) {
273  ind_prefix = 0;
274  state = State::PLACEHOLDER;
275  }
276  }
277  }
278 
279  void do_placeholder(char cur) {
280  placeholder.push_back(cur);
281  if (placeholder.size() == max_placeholder_len) {
282  std::string msg = "Parameter name: [" + placeholder + "] is too long";
283  on_error(msg);
284  }
285  if (cur == postfix[ind_postfix]) {
286  ind_postfix += 1;
287  if (postfix.length() == ind_postfix) {
288  ind_postfix = 0;
289  placeholder.resize(placeholder.length() - postfix.length());
290  auto par = values.find(placeholder);
291  if (values.end() != par) {
292  size_t wp = buffer.size();
293  buffer.resize(wp + par->second.length());
294  std::memcpy(buffer.data() + wp, par->second.c_str(), par->second.length());
295  placeholder.clear();
296  state = State::PREFIX;
297  } else {
298  std::string msg = "Parameter: [" + placeholder + "] not found";
299  on_error(msg);
300  }
301  }
302  } else {
303  ind_postfix = 0;
304  }
305  }
306 
307  std::streamsize copy_buffered(char* buf, size_t ulen) {
308  size_t avail = buffer.size() - pos;
309  if (avail > 0) {
310  size_t ucplen = avail <= ulen ? avail : ulen;
311  std::memcpy(buf, buffer.data() + pos, ucplen);
312  pos += ucplen;
313  return static_cast<std::streamsize>(ucplen);
314  }
315  return 0;
316  }
317 
318 };
319 
327 template <typename Source,
328 class = typename std::enable_if<!std::is_lvalue_reference<Source>::value>::type>
329 replacer_source<Source> make_replacer_source(Source&& source, std::map<std::string, std::string> values,
330  std::function<void(const std::string&)> on_error) {
331  return replacer_source<Source>(std::move(source), std::move(values), on_error);
332 }
333 
341 template <typename Source>
342 replacer_source<reference_source<Source>> make_replacer_source(Source& source, std::map<std::string, std::string> values,
343  std::function<void(const std::string&)> on_error) {
344  return replacer_source<reference_source<Source>>(make_reference_source(source), std::move(values), on_error);
345 }
346 
347 } // namespace
348 }
349 
350 #endif /* STATICLIB_IO_REPLACER_SOURCE_HPP */
351