2015-05-12 20:27:46 +00:00
|
|
|
/* Generic stream implementation for OctaSTD.
|
|
|
|
*
|
|
|
|
* This file is part of OctaSTD. See COPYING.md for futher information.
|
|
|
|
*/
|
|
|
|
|
2015-07-13 19:08:55 +00:00
|
|
|
#ifndef OSTD_STREAM_HH
|
|
|
|
#define OSTD_STREAM_HH
|
2015-05-12 20:27:46 +00:00
|
|
|
|
2017-03-09 18:21:01 +00:00
|
|
|
#include <cstdlib>
|
2017-02-09 19:56:15 +00:00
|
|
|
#include <type_traits>
|
2017-02-26 01:06:02 +00:00
|
|
|
#include <locale>
|
2017-02-27 16:52:52 +00:00
|
|
|
#include <optional>
|
2017-03-09 18:21:01 +00:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <system_error>
|
|
|
|
|
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
|
|
|
#include <sys/types.h>
|
|
|
|
#endif
|
2017-01-25 00:44:22 +00:00
|
|
|
|
2015-07-13 19:08:55 +00:00
|
|
|
#include "ostd/platform.hh"
|
|
|
|
#include "ostd/types.hh"
|
|
|
|
#include "ostd/range.hh"
|
|
|
|
#include "ostd/string.hh"
|
|
|
|
#include "ostd/format.hh"
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2015-07-13 19:07:14 +00:00
|
|
|
namespace ostd {
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2015-07-13 19:08:55 +00:00
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
2017-02-16 19:39:05 +00:00
|
|
|
using stream_off_t = off_t;
|
2015-07-01 19:59:05 +00:00
|
|
|
#else
|
2017-02-16 19:39:05 +00:00
|
|
|
using stream_off_t = __int64;
|
2015-07-01 19:59:05 +00:00
|
|
|
#endif
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
enum class stream_seek {
|
|
|
|
CUR = SEEK_CUR,
|
|
|
|
END = SEEK_END,
|
|
|
|
SET = SEEK_SET
|
2015-06-26 01:30:48 +00:00
|
|
|
};
|
|
|
|
|
2017-02-09 19:56:15 +00:00
|
|
|
template<typename T = char, bool = std::is_pod_v<T>>
|
2017-02-16 19:02:55 +00:00
|
|
|
struct stream_range;
|
2015-06-26 20:14:54 +00:00
|
|
|
|
2017-03-02 17:12:00 +00:00
|
|
|
struct stream_error: std::system_error {
|
|
|
|
using std::system_error::system_error;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T = char, typename TC = std::basic_string<T>>
|
|
|
|
struct stream_line_range;
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
struct stream {
|
|
|
|
using offset_type = stream_off_t;
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
virtual ~stream() {}
|
2015-06-26 19:18:40 +00:00
|
|
|
|
2015-06-26 01:30:48 +00:00
|
|
|
virtual void close() = 0;
|
|
|
|
|
2015-06-26 19:18:40 +00:00
|
|
|
virtual bool end() const = 0;
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
virtual offset_type size() {
|
|
|
|
offset_type p = tell();
|
2017-02-19 15:45:06 +00:00
|
|
|
seek(0, stream_seek::END);
|
2017-02-16 19:39:05 +00:00
|
|
|
offset_type e = tell();
|
2017-02-19 15:45:06 +00:00
|
|
|
if (p == e) {
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
seek(p, stream_seek::SET);
|
|
|
|
return e;
|
2015-06-26 01:30:48 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
virtual void seek(offset_type, stream_seek = stream_seek::SET) {}
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
virtual offset_type tell() const { return -1; }
|
2015-06-26 01:30:48 +00:00
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
virtual void flush() {}
|
2015-06-26 19:18:40 +00:00
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
virtual void read_bytes(void *, size_t) {}
|
|
|
|
virtual void write_bytes(void const *, size_t) {}
|
2015-06-26 19:18:40 +00:00
|
|
|
|
2017-03-02 17:12:00 +00:00
|
|
|
virtual int get_char() {
|
2015-07-05 22:59:36 +00:00
|
|
|
byte c;
|
2017-02-19 15:45:06 +00:00
|
|
|
read_bytes(&c, 1);
|
|
|
|
return c;
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 17:12:00 +00:00
|
|
|
virtual void put_char(int c) {
|
2015-07-05 22:59:36 +00:00
|
|
|
byte wc = byte(c);
|
2017-02-19 15:45:06 +00:00
|
|
|
write_bytes(&wc, 1);
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 17:12:00 +00:00
|
|
|
template<typename T = char, typename R>
|
|
|
|
void get_line(R &&writer, bool keep_nl = false) {
|
|
|
|
bool cr = false;
|
|
|
|
/* read one char, if it fails to read at all just propagate errors */
|
|
|
|
T c = get<T>();
|
|
|
|
bool gotc = false;
|
|
|
|
do {
|
|
|
|
if (cr) {
|
|
|
|
writer.put('\r');
|
|
|
|
cr = false;
|
|
|
|
}
|
|
|
|
if (c == '\r') {
|
|
|
|
cr = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
writer.put(c);
|
|
|
|
gotc = safe_get<T>(c);
|
|
|
|
} while (gotc && (c != '\n'));
|
|
|
|
if (cr && (!gotc || keep_nl)) {
|
|
|
|
/* we had carriage return and either reached EOF
|
|
|
|
* or were told to keep separator, write the CR
|
|
|
|
*/
|
|
|
|
writer.put('\r');
|
|
|
|
}
|
|
|
|
if (gotc && keep_nl) {
|
|
|
|
writer.put('\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 21:51:42 +00:00
|
|
|
template<typename ...A>
|
|
|
|
void write(A const &...args);
|
2015-06-30 22:07:28 +00:00
|
|
|
|
2017-03-02 21:51:42 +00:00
|
|
|
template<typename ...A>
|
|
|
|
void writeln(A const &...args) {
|
2017-02-11 00:28:14 +00:00
|
|
|
write(args...);
|
2017-03-02 17:12:00 +00:00
|
|
|
put_char('\n');
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ...A>
|
2017-02-16 17:48:14 +00:00
|
|
|
void writef(string_range fmt, A const &...args);
|
2015-07-01 17:51:39 +00:00
|
|
|
|
|
|
|
template<typename ...A>
|
2017-02-16 17:48:14 +00:00
|
|
|
void writefln(string_range fmt, A const &...args) {
|
2017-02-11 00:28:14 +00:00
|
|
|
writef(fmt, args...);
|
2017-03-02 17:12:00 +00:00
|
|
|
put_char('\n');
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:02:28 +00:00
|
|
|
template<typename T = char>
|
2017-02-16 19:02:55 +00:00
|
|
|
stream_range<T> iter();
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2017-03-02 17:12:00 +00:00
|
|
|
template<typename T = char, typename TC = std::basic_string<T>>
|
|
|
|
stream_line_range<T, TC> iter_lines(bool keep_nl = false);
|
|
|
|
|
2016-07-31 19:40:25 +00:00
|
|
|
template<typename T>
|
2017-02-19 15:45:06 +00:00
|
|
|
void put(T const *v, size_t count) {
|
|
|
|
write_bytes(v, count * sizeof(T));
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-07-31 19:40:25 +00:00
|
|
|
template<typename T>
|
2017-02-19 15:45:06 +00:00
|
|
|
void put(T v) {
|
|
|
|
write_bytes(&v, sizeof(T));
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-07-31 19:40:25 +00:00
|
|
|
template<typename T>
|
2017-02-19 15:45:06 +00:00
|
|
|
void get(T *v, size_t count) {
|
|
|
|
read_bytes(v, count * sizeof(T));
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-07-31 19:40:25 +00:00
|
|
|
template<typename T>
|
2017-02-19 15:45:06 +00:00
|
|
|
void get(T &v) {
|
|
|
|
read_bytes(&v, sizeof(T));
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-07-31 19:40:25 +00:00
|
|
|
template<typename T>
|
|
|
|
T get() {
|
2015-06-30 18:25:40 +00:00
|
|
|
T r;
|
2017-02-19 15:45:06 +00:00
|
|
|
get(r);
|
|
|
|
return r;
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
2017-02-26 01:06:02 +00:00
|
|
|
|
|
|
|
std::locale imbue(std::locale const &loc) {
|
|
|
|
std::locale ret{p_loc};
|
|
|
|
p_loc = loc;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::locale getloc() const {
|
|
|
|
return p_loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2017-03-02 17:12:00 +00:00
|
|
|
/* helper for get_line, so we don't catch errors from output range put */
|
|
|
|
template<typename T>
|
|
|
|
bool safe_get(T &c) {
|
|
|
|
try {
|
|
|
|
c = get<T>();
|
|
|
|
return true;
|
|
|
|
} catch (stream_error const &) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-26 01:06:02 +00:00
|
|
|
std::locale p_loc;
|
2015-06-26 20:14:54 +00:00
|
|
|
};
|
|
|
|
|
2017-02-13 22:04:02 +00:00
|
|
|
template<typename T>
|
2017-02-16 19:02:55 +00:00
|
|
|
struct stream_range<T, true>: input_range<stream_range<T>> {
|
|
|
|
using range_category = input_range_tag;
|
|
|
|
using value_type = T;
|
|
|
|
using reference = T;
|
|
|
|
using size_type = size_t;
|
2017-02-16 19:39:05 +00:00
|
|
|
using difference_type = stream_off_t;
|
2017-02-13 22:04:02 +00:00
|
|
|
|
2017-02-16 19:02:55 +00:00
|
|
|
stream_range() = delete;
|
2017-02-27 16:52:52 +00:00
|
|
|
stream_range(stream &s): p_stream(&s) {}
|
2017-02-27 16:55:05 +00:00
|
|
|
stream_range(stream_range const &r):
|
|
|
|
p_stream(r.p_stream), p_item(r.p_item)
|
|
|
|
{}
|
2015-06-26 19:18:40 +00:00
|
|
|
|
2015-06-27 02:19:00 +00:00
|
|
|
bool empty() const {
|
2017-02-27 16:52:52 +00:00
|
|
|
if (!p_item.has_value()) {
|
|
|
|
try {
|
|
|
|
p_item = p_stream->get<T>();
|
2017-03-02 17:12:00 +00:00
|
|
|
} catch (stream_error const &) {
|
2017-02-27 16:52:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-02-19 15:45:06 +00:00
|
|
|
}
|
2017-02-27 16:52:52 +00:00
|
|
|
return false;
|
2015-06-27 02:19:00 +00:00
|
|
|
}
|
2015-06-26 19:18:40 +00:00
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void pop_front() {
|
2017-02-27 16:52:52 +00:00
|
|
|
if (p_item.has_value()) {
|
|
|
|
p_item = std::nullopt;
|
|
|
|
} else {
|
|
|
|
p_stream->get<T>();
|
|
|
|
}
|
2015-06-26 19:18:40 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:02:28 +00:00
|
|
|
T front() const {
|
2017-02-27 16:52:52 +00:00
|
|
|
if (p_item.has_value()) {
|
|
|
|
return p_item.value();
|
|
|
|
} else {
|
|
|
|
return (p_item = p_stream->get<T>()).value();
|
|
|
|
}
|
2015-06-26 19:18:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void put(T val) {
|
2017-02-27 16:52:52 +00:00
|
|
|
p_stream->put(val);
|
2015-07-01 20:46:58 +00:00
|
|
|
}
|
|
|
|
|
2015-06-26 20:14:54 +00:00
|
|
|
private:
|
2017-02-16 19:39:05 +00:00
|
|
|
stream *p_stream;
|
2017-02-27 16:52:52 +00:00
|
|
|
mutable std::optional<T> p_item;
|
2015-06-26 19:18:40 +00:00
|
|
|
};
|
|
|
|
|
2015-06-27 00:02:28 +00:00
|
|
|
template<typename T>
|
2017-02-16 19:39:05 +00:00
|
|
|
inline stream_range<T> stream::iter() {
|
2017-03-02 17:12:00 +00:00
|
|
|
return stream_range<T>{*this};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T, typename TC>
|
|
|
|
struct stream_line_range: input_range<stream_line_range<T, TC>> {
|
|
|
|
using range_category = input_range_tag;
|
|
|
|
using value_type = TC;
|
|
|
|
using reference = TC &;
|
|
|
|
using size_type = size_t;
|
|
|
|
using difference_type = stream_off_t;
|
|
|
|
|
|
|
|
stream_line_range() = delete;
|
|
|
|
stream_line_range(stream &s, bool keep_nl = false):
|
|
|
|
p_stream(&s), p_has_item(false), p_keep_nl(keep_nl)
|
|
|
|
{}
|
|
|
|
stream_line_range(stream_line_range const &r):
|
|
|
|
p_stream(r.p_stream), p_item(r.p_item),
|
|
|
|
p_has_item(r.p_has_item), p_keep_nl(r.p_keep_nl)
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool empty() const {
|
|
|
|
if (!p_has_item) {
|
|
|
|
try {
|
|
|
|
p_item.clear();
|
|
|
|
p_stream->get_line(p_item, p_keep_nl);
|
|
|
|
p_has_item = true;
|
|
|
|
} catch (stream_error const &) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pop_front() {
|
|
|
|
if (p_has_item) {
|
|
|
|
p_item.clear();
|
|
|
|
p_has_item = false;
|
|
|
|
} else {
|
|
|
|
p_stream->get_line(noop_output_range<T>{});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reference front() const {
|
|
|
|
if (p_has_item) {
|
|
|
|
return p_item.get();
|
|
|
|
} else {
|
|
|
|
p_stream->get_line(p_item, p_keep_nl);
|
|
|
|
p_has_item = true;
|
|
|
|
return p_item.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
stream *p_stream;
|
|
|
|
mutable appender_range<TC> p_item;
|
|
|
|
mutable bool p_has_item;
|
|
|
|
bool p_keep_nl;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T, typename TC>
|
2017-03-02 21:51:42 +00:00
|
|
|
inline stream_line_range<T, TC> stream::iter_lines(bool keep_nl) {
|
2017-03-02 17:12:00 +00:00
|
|
|
return stream_line_range<T, TC>{*this, keep_nl};
|
2015-06-26 20:14:54 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 21:51:42 +00:00
|
|
|
template<typename ...A>
|
|
|
|
inline void stream::write(A const &...args) {
|
|
|
|
format_spec sp{'s', p_loc};
|
|
|
|
(sp.format_value(iter(), args), ...);
|
2017-02-10 16:44:06 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 18:29:42 +00:00
|
|
|
template<typename ...A>
|
2017-02-16 19:39:05 +00:00
|
|
|
inline void stream::writef(string_range fmt, A const &...args) {
|
2017-03-02 21:51:42 +00:00
|
|
|
format_spec{fmt, p_loc}.format(iter(), args...);
|
2017-02-01 18:29:42 +00:00
|
|
|
}
|
|
|
|
|
2015-05-12 20:27:46 +00:00
|
|
|
}
|
|
|
|
|
2016-02-07 21:17:15 +00:00
|
|
|
#endif
|