2015-06-30 18:25:40 +00:00
|
|
|
/* Standard I/O 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_IO_HH
|
|
|
|
#define OSTD_IO_HH
|
2015-06-30 18:25:40 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2017-01-25 00:44:22 +00:00
|
|
|
#include <vector>
|
2017-02-19 15:45:06 +00:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <system_error>
|
2017-01-25 00:44:22 +00:00
|
|
|
|
2015-07-13 19:08:55 +00:00
|
|
|
#include "ostd/platform.hh"
|
|
|
|
#include "ostd/string.hh"
|
|
|
|
#include "ostd/stream.hh"
|
|
|
|
#include "ostd/format.hh"
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2015-07-13 19:07:14 +00:00
|
|
|
namespace ostd {
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
enum class stream_mode {
|
|
|
|
READ = 0, WRITE, APPEND, READ_U, WRITE_U, APPEND_U
|
2015-06-30 18:25:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
namespace detail {
|
2016-06-23 18:18:35 +00:00
|
|
|
static char const *filemodes[] = {
|
2016-03-26 17:26:37 +00:00
|
|
|
"rb", "wb", "ab", "rb+", "wb+", "ab+"
|
2015-06-30 18:25:40 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
/* TODO: inherit from system_error and come up with the proper infra for it */
|
|
|
|
struct io_error: std::runtime_error {
|
|
|
|
using std::runtime_error::runtime_error;
|
|
|
|
};
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
struct file_stream: stream {
|
|
|
|
file_stream(): p_f(), p_owned(false) {}
|
|
|
|
file_stream(file_stream const &) = delete;
|
|
|
|
file_stream(file_stream &&s): p_f(s.p_f), p_owned(s.p_owned) {
|
2015-06-30 18:25:40 +00:00
|
|
|
s.p_f = nullptr;
|
|
|
|
s.p_owned = false;
|
|
|
|
}
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
file_stream(string_range path, stream_mode mode = stream_mode::READ): p_f() {
|
2015-06-30 18:25:40 +00:00
|
|
|
open(path, mode);
|
|
|
|
}
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
file_stream(FILE *f): p_f(f), p_owned(false) {}
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
~file_stream() { close(); }
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
file_stream &operator=(file_stream const &) = delete;
|
|
|
|
file_stream &operator=(file_stream &&s) {
|
2015-06-30 18:25:40 +00:00
|
|
|
close();
|
|
|
|
swap(s);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
bool open(string_range path, stream_mode mode = stream_mode::READ) {
|
2016-07-31 19:40:25 +00:00
|
|
|
if (p_f || (path.size() > FILENAME_MAX)) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-07-21 21:30:53 +00:00
|
|
|
char buf[FILENAME_MAX + 1];
|
|
|
|
memcpy(buf, &path[0], path.size());
|
|
|
|
buf[path.size()] = '\0';
|
2016-07-06 17:31:21 +00:00
|
|
|
p_owned = false;
|
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
2017-01-30 18:11:39 +00:00
|
|
|
p_f = fopen(buf, detail::filemodes[size_t(mode)]);
|
2016-07-06 17:31:21 +00:00
|
|
|
#else
|
2017-01-30 18:11:39 +00:00
|
|
|
if (fopen_s(&p_f, buf, detail::filemodes[size_t(mode)]) != 0) {
|
2016-07-06 17:31:21 +00:00
|
|
|
return false;
|
2016-07-31 19:40:25 +00:00
|
|
|
}
|
2016-07-06 17:31:21 +00:00
|
|
|
#endif
|
|
|
|
p_owned = !!p_f;
|
2015-06-30 18:25:40 +00:00
|
|
|
return is_open();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool open(FILE *f) {
|
2016-07-31 19:40:25 +00:00
|
|
|
if (p_f) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
p_f = f;
|
|
|
|
p_owned = false;
|
|
|
|
return is_open();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_open() const { return p_f != nullptr; }
|
|
|
|
bool is_owned() const { return p_owned; }
|
|
|
|
|
|
|
|
void close() {
|
2016-07-31 19:40:25 +00:00
|
|
|
if (p_f && p_owned) {
|
|
|
|
fclose(p_f);
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
p_f = nullptr;
|
|
|
|
p_owned = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool end() const {
|
|
|
|
return feof(p_f) != 0;
|
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void seek(stream_off_t pos, stream_seek whence = stream_seek::SET) {
|
2015-07-13 19:08:55 +00:00
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
2017-02-19 15:45:06 +00:00
|
|
|
if (fseeko(p_f, pos, int(whence)))
|
2015-07-01 19:59:05 +00:00
|
|
|
#else
|
2017-02-19 15:45:06 +00:00
|
|
|
if (_fseeki64(p_f, pos, int(whence)))
|
2015-07-01 19:59:05 +00:00
|
|
|
#endif
|
2017-02-19 15:45:06 +00:00
|
|
|
{
|
|
|
|
throw io_error{"file_stream seek error"};
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
stream_off_t tell() const {
|
2015-07-13 19:08:55 +00:00
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
2017-02-19 15:45:06 +00:00
|
|
|
auto ret = ftello(p_f);
|
2015-07-01 19:59:05 +00:00
|
|
|
#else
|
2017-02-19 15:45:06 +00:00
|
|
|
auto ret = _ftelli64(p_f);
|
2015-07-01 19:59:05 +00:00
|
|
|
#endif
|
2017-02-19 15:45:06 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
throw io_error{"file_stream tell error"};
|
|
|
|
}
|
|
|
|
return ret;
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void flush() {
|
|
|
|
if (fflush(p_f)) {
|
|
|
|
throw io_error{"file_stream flush error"};
|
|
|
|
}
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void read_bytes(void *buf, size_t count) {
|
|
|
|
if (fread(buf, 1, count, p_f) != count) {
|
|
|
|
throw io_error{"file_stream read error"};
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void write_bytes(void const *buf, size_t count) {
|
|
|
|
if (fwrite(buf, 1, count, p_f) != count) {
|
|
|
|
throw io_error{"file_stream write error"};
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 19:59:05 +00:00
|
|
|
int getchar() {
|
2017-02-19 15:45:06 +00:00
|
|
|
int ret = fgetc(p_f);
|
|
|
|
if (ret == EOF) {
|
|
|
|
throw io_error{"file_stream EOF"};
|
|
|
|
}
|
|
|
|
return ret;
|
2015-07-01 19:59:05 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 15:45:06 +00:00
|
|
|
void putchar(int c) {
|
|
|
|
if (fputc(c, p_f) == EOF) {
|
|
|
|
throw io_error{"file_stream EOF"};
|
|
|
|
}
|
2015-07-01 19:59:05 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
void swap(file_stream &s) {
|
2017-01-29 14:56:02 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_f, s.p_f);
|
|
|
|
swap(p_owned, s.p_owned);
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FILE *get_file() { return p_f; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
FILE *p_f;
|
|
|
|
bool p_owned;
|
|
|
|
};
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
inline void swap(file_stream &a, file_stream &b) {
|
2017-01-29 14:56:02 +00:00
|
|
|
a.swap(b);
|
|
|
|
}
|
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
static file_stream in(stdin);
|
|
|
|
static file_stream out(stdout);
|
|
|
|
static file_stream err(stderr);
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
/* no need to call anything from file_stream, prefer simple calls... */
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2015-07-21 21:21:54 +00:00
|
|
|
namespace detail {
|
2017-02-01 18:29:42 +00:00
|
|
|
/* lightweight output range for direct stdout */
|
2017-02-16 19:39:05 +00:00
|
|
|
struct stdout_range: output_range<stdout_range> {
|
2017-02-16 19:02:55 +00:00
|
|
|
using value_type = char;
|
|
|
|
using reference = char &;
|
|
|
|
using size_type = size_t;
|
|
|
|
using difference_type = ptrdiff_t;
|
2017-02-13 22:04:02 +00:00
|
|
|
|
2017-02-16 19:39:05 +00:00
|
|
|
stdout_range() {}
|
2017-02-19 15:45:06 +00:00
|
|
|
void put(char c) {
|
|
|
|
if (putchar(c) == EOF) {
|
|
|
|
throw io_error{"stdout EOF"};
|
|
|
|
}
|
2017-02-01 18:29:42 +00:00
|
|
|
}
|
|
|
|
};
|
2017-02-19 17:46:43 +00:00
|
|
|
|
|
|
|
template<typename R>
|
|
|
|
inline void range_put_all(stdout_range &r, R range) {
|
|
|
|
if constexpr(
|
|
|
|
is_contiguous_range<R> &&
|
|
|
|
std::is_same_v<std::remove_const_t<range_value_t<R>>, char>
|
|
|
|
) {
|
|
|
|
if (fwrite(range.data(), 1, range.size(), stdout) != range.size()) {
|
|
|
|
throw io_error{"stdout write error"};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (; !range.empty(); range.pop_front()) {
|
|
|
|
r.put(range.front());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-30 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
2015-06-30 21:41:01 +00:00
|
|
|
template<typename T>
|
2016-06-23 18:18:35 +00:00
|
|
|
inline void write(T const &v) {
|
2017-02-20 17:04:03 +00:00
|
|
|
format_spec{'s'}.format_value(detail::stdout_range{}, v);
|
2015-06-30 21:41:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T, typename ...A>
|
2016-06-23 18:18:35 +00:00
|
|
|
inline void write(T const &v, A const &...args) {
|
2015-07-05 22:59:36 +00:00
|
|
|
write(v);
|
2015-06-30 21:41:01 +00:00
|
|
|
write(args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
2016-06-23 18:18:35 +00:00
|
|
|
inline void writeln(T const &v) {
|
2015-07-21 21:21:54 +00:00
|
|
|
write(v);
|
2017-02-11 00:28:14 +00:00
|
|
|
if (putchar('\n') == EOF) {
|
2017-02-19 15:45:06 +00:00
|
|
|
throw io_error{"stdout EOF"};
|
2017-02-11 00:28:14 +00:00
|
|
|
}
|
2015-06-30 21:41:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T, typename ...A>
|
2016-06-23 18:18:35 +00:00
|
|
|
inline void writeln(T const &v, A const &...args) {
|
2015-07-05 22:59:36 +00:00
|
|
|
write(v);
|
2015-06-30 21:41:01 +00:00
|
|
|
write(args...);
|
2017-02-11 00:28:14 +00:00
|
|
|
if (putchar('\n') == EOF) {
|
2017-02-19 15:45:06 +00:00
|
|
|
throw io_error{"stdout EOF"};
|
2017-02-11 00:28:14 +00:00
|
|
|
}
|
2015-06-30 21:41:01 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 01:22:42 +00:00
|
|
|
template<typename ...A>
|
2017-02-16 17:48:14 +00:00
|
|
|
inline void writef(string_range fmt, A const &...args) {
|
2017-02-16 19:39:05 +00:00
|
|
|
format(detail::stdout_range{}, fmt, args...);
|
2015-07-01 01:22:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ...A>
|
2017-02-16 17:48:14 +00:00
|
|
|
inline void writefln(string_range fmt, A const &...args) {
|
2015-07-01 17:51:39 +00:00
|
|
|
writef(fmt, args...);
|
2017-02-11 00:28:14 +00:00
|
|
|
if (putchar('\n') == EOF) {
|
2017-02-19 15:45:06 +00:00
|
|
|
throw io_error{"stdout EOF"};
|
2017-02-11 00:28:14 +00:00
|
|
|
}
|
2015-07-01 01:22:42 +00:00
|
|
|
}
|
|
|
|
|
2015-07-13 19:07:14 +00:00
|
|
|
} /* namespace ostd */
|
2015-06-30 18:25:40 +00:00
|
|
|
|
2016-02-07 21:17:15 +00:00
|
|
|
#endif
|