staticlib_io  1.2.11
 All Classes Namespaces Files Functions Variables
buffered_source.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015, 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: buffered_source.hpp
19  * Author: alex
20  *
21  * Created on October 9, 2015, 12:15 PM
22  */
23 
24 #ifndef STATICLIB_IO_BUFFERED_SOURCE_HPP
25 #define STATICLIB_IO_BUFFERED_SOURCE_HPP
26 
27 #include <cstring>
28 #include <array>
29 #include <ios>
30 #include <string>
31 #include <utility>
32 
33 #include "staticlib/config.hpp"
34 
37 #include "staticlib/io/span.hpp"
38 
39 namespace staticlib {
40 namespace io {
41 
45 template <typename Source, std::size_t buf_size = 4096>
50  Source src;
54  bool exhausted = false;
55 
59  std::array<char, buf_size> buffer;
63  size_t pos = 0;
67  size_t avail = 0;
68 
69 public:
76  explicit buffered_source(Source&& src) :
77  src(std::move(src)) { }
78 
84  buffered_source(const buffered_source&) = delete;
85 
92  buffered_source& operator=(const buffered_source&) = delete;
93 
99  buffered_source(buffered_source&& other) STATICLIB_NOEXCEPT :
100  src(std::move(other.src)),
101  exhausted(other.exhausted),
102  buffer(std::move(other.buffer)),
103  pos(other.pos),
104  avail(other.avail) {
105  other.exhausted = true;
106  other.pos = 0;
107  other.avail = 0;
108  }
109 
116  buffered_source& operator=(buffered_source&& other) STATICLIB_NOEXCEPT {
117  src = std::move(other.src);
118  exhausted = other.exhausted;
119  other.exhausted = true;
120  buffer = std::move(other.buffer);
121  pos = other.pos;
122  other.pos = 0;
123  avail = other.avail;
124  other.avail = 0;
125  return *this;
126  }
127 
134  std::streamsize read(span<char> span) {
135  size_t ulen = span.size();
136  // return from buffer
137  if (ulen <= avail) {
138  std::memcpy(span.data(), buffer.data() + pos, ulen);
139  pos += ulen;
140  avail -= ulen;
141  return span.size_signed();
142  }
143  // copy all data already available
144  size_t uhead = avail;
145  if (!sl::support::is_streamsize(uhead)) {
146  uhead = static_cast<size_t> (std::numeric_limits<std::streamsize>::max());
147  }
148  if (uhead > 0) {
149  std::memcpy(span.data(), buffer.data() + pos, uhead);
150  }
151  pos = 0;
152  avail = 0;
153  std::streamsize head = static_cast<std::streamsize> (uhead);
154  // try to guess whether to do direct read, or fill buffer first
155  if (ulen > buffer.size()) {
156  // read directly into the destination
157  size_t result = read_into_buffer(span.data(), uhead, ulen - uhead);
158  size_t out = result + uhead;
159  return out > 0 ? out : std::char_traits<char>::eof();
160  }
161  // fill buffer
162  avail = read_into_buffer(buffer.data(), 0, buffer.size());
163  if (avail > 0) {
164  // copy tail from buffer
165  size_t to_read_req = ulen - uhead;
166  size_t to_read = to_read_req <= avail ? to_read_req : avail;
167  if (!sl::support::is_streamsize(to_read + uhead)) {
168  to_read = static_cast<size_t> (std::numeric_limits<std::streamsize>::max()) - uhead;
169  }
170  std::memcpy(span.data() + uhead, buffer.data(), to_read);
171  pos = to_read;
172  avail -= to_read;
173  // note that head >= 0, and to_read > 0
174  return static_cast<std::streamsize>(to_read + uhead);
175  }
176  return head == 0 ? std::char_traits<char>::eof() : head;
177  }
178 
188  std::string read_line(const std::string& ending = "\n", size_t max_length = (1<<16)) {
189  std::string line;
190  char ch;
191  while(line.length() < max_length && 1 == read({std::addressof(ch), 1})) {
192  line.push_back(ch);
193  if (ending.length() > 0 && ch == ending.back() && line.length() >= ending.length()) {
194  bool matched = true;
195  size_t sdiff = line.length() - ending.length();
196  for (int i = static_cast<int>(ending.length()) - 2; i >= 0; i--) {
197  if (ending[i] != line[sdiff + i]) {
198  matched = false;
199  break;
200  }
201  }
202  if (matched) {
203  for (size_t i = 0; i < ending.length(); i++) {
204  line.pop_back();
205  }
206  if (line.length() > 0) {
207  break;
208  }
209  }
210  }
211  }
212  return line;
213  }
214 
220  Source& get_source() {
221  return src;
222  }
223 
229  std::array<char, buf_size>& get_buffer() {
230  return buffer;
231  }
232 
233 private:
234  // repeatable source read logic
235  size_t read_into_buffer(char* buf, size_t offset, size_t length) {
236  if (!exhausted) {
237  size_t result = 0;
238  while (result < length) {
239  size_t ulen = length - result;
240  if (!sl::support::is_streamsize(ulen)) {
241  ulen = static_cast<size_t> (std::numeric_limits<std::streamsize>::max());
242  }
243  std::streamsize amt = src.read({buf + offset + result, static_cast<std::streamsize> (ulen)});
244  if (std::char_traits<char>::eof() != amt) {
245  if (!sl::support::is_sizet(amt)) throw io_exception(TRACEMSG(
246  "Invalid result returned by underlying 'read' operation: [" + sl::support::to_string(amt) + "]"));
247  result += static_cast<size_t> (amt);
248  } else {
249  exhausted = true;
250  break;
251  }
252  }
253  return result;
254  }
255  return 0;
256  }
257 };
258 
266 template <typename Source,
267  class = typename std::enable_if<!std::is_lvalue_reference<Source>::value>::type>
269  return buffered_source<Source>(std::move(source));
270 }
271 
279 template <typename Source>
282 }
283 
284 } // namespace
285 }
286 
287 #endif /* STATICLIB_IO_BUFFERED_SOURCE_HPP */
288