summaryrefslogtreecommitdiff
path: root/src/io
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-10-25 15:24:26 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-10-26 14:21:48 -0700
commit86395f53634b3ef1ce76a7f1e5edfdb61f8ffd80 (patch)
tree2710c62fe71d5e0d2e228fba9c951a040c4dcddf /src/io
parent6800761863dd45b6055768febc6ace6a20120dc7 (diff)
downloadtmwa-86395f53634b3ef1ce76a7f1e5edfdb61f8ffd80.tar.gz
tmwa-86395f53634b3ef1ce76a7f1e5edfdb61f8ffd80.tar.bz2
tmwa-86395f53634b3ef1ce76a7f1e5edfdb61f8ffd80.tar.xz
tmwa-86395f53634b3ef1ce76a7f1e5edfdb61f8ffd80.zip
Fix header ranking
Diffstat (limited to 'src/io')
-rw-r--r--src/io/cxxstdio_enums.hpp77
-rw-r--r--src/io/dir.hpp2
-rw-r--r--src/io/extract.cpp220
-rw-r--r--src/io/extract.hpp232
-rw-r--r--src/io/extract_test.cpp452
-rw-r--r--src/io/fd.hpp2
-rw-r--r--src/io/fwd.hpp6
-rw-r--r--src/io/read.cpp2
-rw-r--r--src/io/read.hpp2
-rw-r--r--src/io/write.hpp2
10 files changed, 911 insertions, 86 deletions
diff --git a/src/io/cxxstdio_enums.hpp b/src/io/cxxstdio_enums.hpp
deleted file mode 100644
index 6f428e8..0000000
--- a/src/io/cxxstdio_enums.hpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-// cxxstdio_enums.hpp - Opt-in integer formatting support for enums.
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "fwd.hpp"
-
-#include <cstdint>
-
-#include "../generic/enum.hpp"
-
-
-namespace tmwa
-{
-namespace e
-{
-enum class BF : uint16_t;
-enum class EPOS : uint16_t;
-enum class MapCell : uint8_t;
-enum class Opt0 : uint16_t;
-
-inline
-auto decay_for_printf(BF v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(EPOS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(MapCell v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(Opt0 v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-}
-
-namespace magic
-{
-enum class SPELLARG : uint8_t;
-
-inline
-auto decay_for_printf(SPELLARG v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-}
-
-enum class BL : uint8_t;
-enum class ByteCode : uint8_t;
-enum class ItemLook : uint16_t;
-enum class MS : uint8_t;
-enum class SP : uint16_t;
-enum class SkillID : uint16_t;
-enum class StatusChange : uint16_t;
-
-inline
-auto decay_for_printf(BL v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(ByteCode v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(ItemLook v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(MS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(SP v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(SkillID v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(StatusChange v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-} // namespace tmwa
diff --git a/src/io/dir.hpp b/src/io/dir.hpp
index 071f309..f6fedbf 100644
--- a/src/io/dir.hpp
+++ b/src/io/dir.hpp
@@ -20,8 +20,6 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
#include "fd.hpp"
diff --git a/src/io/extract.cpp b/src/io/extract.cpp
new file mode 100644
index 0000000..55d3980
--- /dev/null
+++ b/src/io/extract.cpp
@@ -0,0 +1,220 @@
+#include "extract.hpp"
+// extract.cpp - a simple, hierarchical, tokenizer
+//
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <algorithm>
+
+#include "../strings/astring.hpp"
+#include "../strings/xstring.hpp"
+#include "../strings/vstring.hpp"
+
+#include "../poison.hpp"
+
+
+// TODO also pass an io::LineSpan around.
+namespace tmwa
+{
+bool extract(XString str, XString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool extract(XString str, RString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool extract(XString str, AString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool extract(XString str, std::chrono::nanoseconds *ns)
+{
+ std::chrono::nanoseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *ns = std::chrono::nanoseconds(rep);
+ return true;
+ }
+ if (str.endswith("ns"_s))
+ {
+ if (extract(str.xrslice_h("ns"_s.size()), &rep))
+ {
+ *ns = std::chrono::nanoseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::microseconds bigger;
+ if (extract(str, &bigger))
+ {
+ *ns = bigger;
+ return *ns == bigger;
+ }
+ return false;
+}
+bool extract(XString str, std::chrono::microseconds *us)
+{
+ std::chrono::microseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *us = std::chrono::microseconds(rep);
+ return true;
+ }
+ if (str.endswith("us"_s))
+ {
+ if (extract(str.xrslice_h("us"_s.size()), &rep))
+ {
+ *us = std::chrono::microseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::milliseconds bigger;
+ if (extract(str, &bigger))
+ {
+ *us = bigger;
+ return *us == bigger;
+ }
+ return false;
+}
+bool extract(XString str, std::chrono::milliseconds *ms)
+{
+ std::chrono::milliseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *ms = std::chrono::milliseconds(rep);
+ return true;
+ }
+ if (str.endswith("ms"_s))
+ {
+ if (extract(str.xrslice_h("ms"_s.size()), &rep))
+ {
+ *ms = std::chrono::milliseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::seconds bigger;
+ if (extract(str, &bigger))
+ {
+ *ms = bigger;
+ return *ms == bigger;
+ }
+ return false;
+}
+bool extract(XString str, std::chrono::seconds *s)
+{
+ std::chrono::seconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *s = std::chrono::seconds(rep);
+ return true;
+ }
+ if (str.endswith("s"_s))
+ {
+ if (extract(str.xrslice_h("s"_s.size()), &rep))
+ {
+ *s = std::chrono::seconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::minutes bigger;
+ if (extract(str, &bigger))
+ {
+ *s = bigger;
+ return *s == bigger;
+ }
+ return false;
+}
+bool extract(XString str, std::chrono::minutes *min)
+{
+ std::chrono::minutes::rep rep;
+ if (extract(str, &rep))
+ {
+ *min = std::chrono::minutes(rep);
+ return true;
+ }
+ if (str.endswith("min"_s))
+ {
+ if (extract(str.xrslice_h("min"_s.size()), &rep))
+ {
+ *min = std::chrono::minutes(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::hours bigger;
+ if (extract(str, &bigger))
+ {
+ *min = bigger;
+ return *min == bigger;
+ }
+ return false;
+}
+bool extract(XString str, std::chrono::hours *h)
+{
+ std::chrono::hours::rep rep;
+ if (extract(str, &rep))
+ {
+ *h = std::chrono::hours(rep);
+ return true;
+ }
+ if (str.endswith("h"_s))
+ {
+ if (extract(str.xrslice_h("h"_s.size()), &rep))
+ {
+ *h = std::chrono::hours(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::duration<int, std::ratio<60*60*24>> bigger;
+ if (extract(str, &bigger))
+ {
+ *h = bigger;
+ return *h == bigger;
+ }
+ return false;
+}
+bool extract(XString str, std::chrono::duration<int, std::ratio<60*60*24>> *d)
+{
+ std::chrono::duration<int, std::ratio<60*60*24>>::rep rep;
+ if (extract(str, &rep))
+ {
+ *d = std::chrono::duration<int, std::ratio<60*60*24>>(rep);
+ return true;
+ }
+ if (str.endswith("d"_s))
+ {
+ if (extract(str.xrslice_h("d"_s.size()), &rep))
+ {
+ *d = std::chrono::duration<int, std::ratio<60*60*24>>(rep);
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+} // namespace tmwa
diff --git a/src/io/extract.hpp b/src/io/extract.hpp
new file mode 100644
index 0000000..174243e
--- /dev/null
+++ b/src/io/extract.hpp
@@ -0,0 +1,232 @@
+#pragma once
+// extract.hpp - a simple, hierarchical, tokenizer
+//
+// Copyright © 2012-2013 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cerrno>
+#include <cstdlib>
+
+#include <algorithm>
+#include <chrono>
+#include <vector>
+
+#include "../ints/wrap.hpp"
+
+#include "../strings/xstring.hpp"
+
+#include "../compat/time_t.hpp"
+
+#include "../generic/enum.hpp"
+
+
+namespace tmwa
+{
+template<class T>
+bool do_extract(XString str, T t);
+
+template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && !std::is_same<T, bool>::value>::type>
+bool extract(XString str, T *iv)
+{
+ if (!str || str.size() > 20)
+ return false;
+ if (!((str.front() == '-' && std::is_signed<T>::value)
+ || ('0' <= str.front() && str.front() <= '9')))
+ return false;
+ // needs a NUL, but can't always be given one. TODO VString?
+ char buf[20 + 1];
+ std::copy(str.begin(), str.end(), buf);
+ buf[str.size()] = '\0';
+
+ char *end;
+ errno = 0;
+ if (std::is_signed<T>::value)
+ {
+ long long v = strtoll(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+ else
+ {
+ unsigned long long v = strtoull(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+}
+
+inline
+bool extract(XString str, TimeT *tv)
+{
+ return extract(str, &tv->value);
+}
+
+template<class T, typename=typename std::enable_if<std::is_enum<T>::value>::type>
+bool extract_as_int(XString str, T *iv)
+{
+ typedef typename underlying_type<T>::type U;
+ U v;
+ // defer to integer version
+ if (!extract(str, &v))
+ return false;
+ // TODO check bounds using enum min/max as in SSCANF
+ *iv = static_cast<T>(v);
+ return true;
+}
+
+bool extract(XString str, XString *rv);
+bool extract(XString str, RString *rv);
+bool extract(XString str, AString *rv);
+
+template<uint8_t N>
+bool extract(XString str, VString<N> *out)
+{
+ if (str.size() > N)
+ return false;
+ *out = str;
+ return true;
+}
+
+inline
+bool extract(XString str, LString exact)
+{
+ return str == exact;
+}
+
+template<class T>
+class LStripper
+{
+public:
+ T impl;
+};
+
+template<class T>
+LStripper<T> lstripping(T v)
+{
+ return {v};
+}
+
+template<class T>
+bool extract(XString str, LStripper<T> out)
+{
+ return extract(str.lstrip(), out.impl);
+}
+
+// basically just a std::tuple
+// but it provides its data members publically
+template<char split, int n, class... T>
+class Record;
+template<char split, int n>
+class Record<split, n>
+{
+};
+template<char split, int n, class F, class... R>
+class Record<split, n, F, R...>
+{
+public:
+ F frist;
+ Record<split, n - 1, R...> rest;
+public:
+ Record(F f, R... r)
+ : frist(f), rest(r...)
+ {}
+};
+template<char split, class... T>
+Record<split, sizeof...(T), T...> record(T... t)
+{
+ return Record<split, sizeof...(T), T...>(t...);
+}
+template<char split, int n, class... T>
+Record<split, n, T...> record(T... t)
+{
+ static_assert(0 < n && n < sizeof...(T), "don't be silly");
+ return Record<split, n, T...>(t...);
+}
+
+template<char split, int n>
+bool extract(XString str, Record<split, n>)
+{
+ return !str;
+}
+
+template<char split, int n, class F, class... R>
+bool extract(XString str, Record<split, n, F, R...> rec)
+{
+ XString::iterator s = std::find(str.begin(), str.end(), split);
+ XString::iterator s2 = s;
+ if (s2 != str.end())
+ ++s2;
+ XString head = str.xislice_h(s);
+ XString tail = str.xislice_t(s2);
+ if (s == str.end())
+ return (extract(head, rec.frist) && n <= 1)
+ || (!head && n <= 0);
+
+ return (extract(head, rec.frist) || n <= 0)
+ && extract(tail, rec.rest);
+}
+
+template<char split, class T>
+struct VRecord
+{
+ std::vector<T> *arr;
+};
+
+template<char split, class T>
+VRecord<split, T> vrec(std::vector<T> *arr)
+{
+ return {arr};
+}
+
+template<char split, class T>
+bool extract(XString str, VRecord<split, T> rec)
+{
+ if (!str)
+ return true;
+ XString::iterator s = std::find(str.begin(), str.end(), split);
+ rec.arr->emplace_back();
+ if (s == str.end())
+ return extract(str, &rec.arr->back());
+ return extract(str.xislice_h(s), &rec.arr->back())
+ && extract(str.xislice_t(s + 1), rec);
+}
+
+template<class T>
+bool do_extract(XString str, T t)
+{
+ return extract(str, t);
+}
+
+template<class R>
+bool extract(XString str, Wrapped<R> *w)
+{
+ return extract(str, &w->_value);
+}
+
+bool extract(XString str, std::chrono::nanoseconds *ns);
+bool extract(XString str, std::chrono::microseconds *us);
+bool extract(XString str, std::chrono::milliseconds *ms);
+bool extract(XString str, std::chrono::seconds *s);
+bool extract(XString str, std::chrono::minutes *min);
+bool extract(XString str, std::chrono::hours *h);
+bool extract(XString str, std::chrono::duration<int, std::ratio<60*60*24>> *d);
+} // namespace tmwa
diff --git a/src/io/extract_test.cpp b/src/io/extract_test.cpp
new file mode 100644
index 0000000..ee4cb08
--- /dev/null
+++ b/src/io/extract_test.cpp
@@ -0,0 +1,452 @@
+#include "extract.hpp"
+// extract_test.cpp - Testsuite for a simple, hierarchical, tokenizer
+//
+// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../strings/xstring.hpp"
+
+#include "../net/timer.t.hpp"
+
+#include "../mmo/strs.hpp"
+
+#include "../high/extract_mmo.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(extract, record_int)
+{
+ int x, y, z;
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(""_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+}
+
+TEST(extract, record_str)
+{
+ XString x, y, z;
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract(" "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract(" "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract(""_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+}
+
+TEST(extract, mapname)
+{
+ MapName map;
+ EXPECT_TRUE(extract("abc"_s, &map));
+ EXPECT_EQ(map, "abc"_s);
+ EXPECT_TRUE(extract("abc.gat"_s, &map));
+ EXPECT_EQ(map, "abc"_s);
+ EXPECT_TRUE(extract("abcdefghijklmno"_s, &map));
+ EXPECT_EQ(map, "abcdefghijklmno"_s);
+ EXPECT_TRUE(extract("abcdefghijklmno.gat"_s, &map));
+ EXPECT_EQ(map, "abcdefghijklmno"_s);
+}
+
+TEST(extract, chrono)
+{
+ std::chrono::nanoseconds ns;
+ std::chrono::microseconds us;
+ std::chrono::milliseconds ms;
+ std::chrono::seconds s;
+ std::chrono::minutes min;
+ std::chrono::hours h;
+ std::chrono::duration<int, std::ratio<60*60*24>> d;
+
+ EXPECT_TRUE(extract("1"_s, &ns));
+ EXPECT_EQ(ns, 1_ns);
+ EXPECT_TRUE(extract("3ns"_s, &ns));
+ EXPECT_EQ(ns, 3_ns);
+ EXPECT_TRUE(extract("4us"_s, &ns));
+ EXPECT_EQ(ns, 4_us);
+ EXPECT_TRUE(extract("5ms"_s, &ns));
+ EXPECT_EQ(ns, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &ns));
+ EXPECT_EQ(ns, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &ns));
+ EXPECT_EQ(ns, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &ns));
+ EXPECT_EQ(ns, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &ns));
+ EXPECT_EQ(ns, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &us));
+ EXPECT_EQ(us, 1_us);
+ EXPECT_TRUE(extract("4us"_s, &us));
+ EXPECT_EQ(us, 4_us);
+ EXPECT_TRUE(extract("5ms"_s, &us));
+ EXPECT_EQ(us, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &us));
+ EXPECT_EQ(us, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &us));
+ EXPECT_EQ(us, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &us));
+ EXPECT_EQ(us, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &us));
+ EXPECT_EQ(us, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &ms));
+ EXPECT_EQ(ms, 1_ms);
+ EXPECT_TRUE(extract("5ms"_s, &ms));
+ EXPECT_EQ(ms, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &ms));
+ EXPECT_EQ(ms, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &ms));
+ EXPECT_EQ(ms, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &ms));
+ EXPECT_EQ(ms, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &ms));
+ EXPECT_EQ(ms, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &s));
+ EXPECT_EQ(s, 1_s);
+ EXPECT_TRUE(extract("6s"_s, &s));
+ EXPECT_EQ(s, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &s));
+ EXPECT_EQ(s, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &s));
+ EXPECT_EQ(s, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &s));
+ EXPECT_EQ(s, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &min));
+ EXPECT_EQ(min, 1_min);
+ EXPECT_TRUE(extract("7min"_s, &min));
+ EXPECT_EQ(min, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &min));
+ EXPECT_EQ(min, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &min));
+ EXPECT_EQ(min, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &h));
+ EXPECT_EQ(h, 1_h);
+ EXPECT_TRUE(extract("8h"_s, &h));
+ EXPECT_EQ(h, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &h));
+ EXPECT_EQ(h, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &d));
+ EXPECT_EQ(d, 1_d);
+ EXPECT_TRUE(extract("9d"_s, &d));
+ EXPECT_EQ(d, 9_d);
+}
+} // namespace tmwa
diff --git a/src/io/fd.hpp b/src/io/fd.hpp
index d04d5bf..03a8b44 100644
--- a/src/io/fd.hpp
+++ b/src/io/fd.hpp
@@ -23,8 +23,6 @@
#include <sys/select.h>
#include <sys/socket.h>
-#include "../strings/fwd.hpp"
-
#include "../diagnostics.hpp"
diff --git a/src/io/fwd.hpp b/src/io/fwd.hpp
index deeb08c..99268f4 100644
--- a/src/io/fwd.hpp
+++ b/src/io/fwd.hpp
@@ -20,6 +20,12 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+// io/fwd.hpp is rank 4
+
namespace tmwa
{
diff --git a/src/io/read.cpp b/src/io/read.cpp
index f3ed293..30620a1 100644
--- a/src/io/read.cpp
+++ b/src/io/read.cpp
@@ -25,7 +25,7 @@
#include "../strings/zstring.hpp"
#include "../strings/literal.hpp"
-#include "../io/cxxstdio.hpp"
+#include "cxxstdio.hpp"
#include "../poison.hpp"
diff --git a/src/io/read.hpp b/src/io/read.hpp
index 1ec26ca..c1c4882 100644
--- a/src/io/read.hpp
+++ b/src/io/read.hpp
@@ -20,8 +20,6 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
#include "dir.hpp"
#include "fd.hpp"
diff --git a/src/io/write.hpp b/src/io/write.hpp
index 1ab05f3..d7d3699 100644
--- a/src/io/write.hpp
+++ b/src/io/write.hpp
@@ -22,8 +22,6 @@
#include <cstdarg>
-#include "../strings/fwd.hpp"
-
#include "dir.hpp"
#include "fd.hpp"