diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters
index 474ac2553..8244730c2 100644
--- a/build/libaegisub/libaegisub.vcxproj.filters
+++ b/build/libaegisub/libaegisub.vcxproj.filters
@@ -295,6 +295,9 @@
Source Files\Common
+
+ Source Files\Common
+
Source Files\Common
diff --git a/libaegisub/Makefile b/libaegisub/Makefile
index 2008256ce..29882968b 100644
--- a/libaegisub/Makefile
+++ b/libaegisub/Makefile
@@ -26,6 +26,7 @@ SRC += \
common/color.cpp \
common/dispatch.cpp \
common/file_mapping.cpp \
+ common/format.cpp \
common/fs.cpp \
common/hotkey.cpp \
common/io.cpp \
diff --git a/libaegisub/common/format.cpp b/libaegisub/common/format.cpp
new file mode 100644
index 000000000..d05e7208e
--- /dev/null
+++ b/libaegisub/common/format.cpp
@@ -0,0 +1,233 @@
+// Copyright (c) 2014, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+
+#include
+
+#include
+#include
+
+namespace {
+template
+int actual_len(int max_len, const Char *value) {
+ int len = 0;
+ while (value[len] && (max_len <= 0 || len < max_len)) ++len;
+ return len;
+}
+
+template
+int actual_len(int max_len, std::basic_string value) {
+ if (max_len > 0 && static_cast(max_len) < value.size())
+ return max_len;
+ return value.size();
+}
+
+template
+void do_write_str(std::basic_ostream& out, const Char *str, int len) {
+ out.write(str, len);
+}
+
+void do_write_str(std::ostream& out, const wchar_t *str, int len) {
+ std::wstring_convert> utf8_conv;
+ auto u8 = utf8_conv.to_bytes(str, str + len);
+ out.write(u8.data(), u8.size());
+}
+
+void do_write_str(std::wostream& out, const char *str, int len) {
+ std::wstring_convert> utf8_conv;
+ auto wc = utf8_conv.from_bytes(str, str + len);
+ out.write(wc.data(), wc.size());
+}
+
+template
+struct format_parser {
+ agi::format_detail::formatter_state &s;
+
+ void read_and_append_up_to_next_specifier() {
+ for (std::streamsize len = 0; ; ++len) {
+ // Ran out of format specifiers; not an error due to that
+ // translated strings may not need them all
+ if (!s.fmt[len]) {
+ s.out.write(s.fmt, len);
+ s.fmt += len;
+ return;
+ }
+
+ if (s.fmt[len] == '%') {
+ if (s.fmt[len + 1] == '%') {
+ s.out.write(s.fmt, len);
+ s.fmt += len + 1;
+ len = 0;
+ continue;
+ }
+
+ s.out.write(s.fmt, len);
+ s.fmt += len;
+ break;
+ }
+ }
+ }
+
+ int read_int() {
+ int i = 0;
+ for (; *s.fmt_cur >= '0' && *s.fmt_cur <= '9'; ++s.fmt_cur)
+ i = 10 * i + (*s.fmt_cur - '0');
+ return i;
+ }
+
+ void parse_flags() {
+ for (; ; ++s.fmt_cur) {
+ switch (*s.fmt_cur) {
+ // Not supported: ' ' (add a space before positive numers to align with negative)
+ case '#':
+ s.out.setf(std::ios::showpoint | std::ios::showbase);
+ continue;
+ case '0':
+ // overridden by left alignment ('-' flag)
+ if (!(s.out.flags() & std::ios::left)) {
+ // Use internal padding so that numeric values are
+ // formatted correctly, eg -00010 rather than 000-10
+ s.out.fill('0');
+ s.out.setf(std::ios::internal, std::ios::adjustfield);
+ }
+ continue;
+ case '-':
+ s.out.fill(' ');
+ s.out.setf(std::ios::left, std::ios::adjustfield);
+ continue;
+ case '+':
+ s.out.setf(std::ios::showpos);
+ continue;
+ }
+ break;
+ }
+ }
+
+ void parse_width() {
+ if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
+ s.width = read_int();
+ else if (*s.fmt_cur == '*') {
+ s.read_width = true;
+ s.pending = true;
+ ++s.fmt_cur;
+ }
+ }
+
+ void parse_precision() {
+ if (*s.fmt_cur != '.') return;
+ ++s.fmt_cur;
+
+ // Ignoring negative precision because it's dumb and pointless
+ if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
+ s.precision = read_int();
+ else if (*s.fmt_cur == '*') {
+ s.read_precision = true;
+ s.pending = true;
+ ++s.fmt_cur;
+ }
+ else
+ s.precision = 0;
+ }
+
+ void parse_length_modifiers() {
+ // Where "parse" means "skip" since we don't need them
+ for (Char c = *s.fmt_cur;
+ c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
+ c = *++s.fmt_cur);
+ }
+
+ void parse_format_specifier() {
+ s.width = 0;
+ s.precision = -1;
+ s.out.fill(' ');
+ s.out.unsetf(
+ std::ios::adjustfield |
+ std::ios::basefield |
+ std::ios::boolalpha |
+ std::ios::floatfield |
+ std::ios::showbase |
+ std::ios::showpoint |
+ std::ios::showpos |
+ std::ios::uppercase);
+
+ // Don't touch fmt until the specifier is fully applied so that if we
+ // have insufficient arguments it'll get passed through to the output
+ s.fmt_cur = s.fmt + 1;
+
+ parse_flags();
+ parse_width();
+ parse_precision();
+ parse_length_modifiers();
+ }
+};
+}
+
+namespace agi {
+
+template
+void writer::write(std::basic_ostream& out, int max_len, const Char *value) {
+ do_write_str(out, value, actual_len(max_len, value));
+}
+
+template
+void writer>::write(std::basic_ostream& out, int max_len, std::basic_string const& value) {
+ do_write_str(out, value.data(), actual_len(max_len, value));
+}
+
+template struct writer;
+template struct writer;
+template struct writer;
+template struct writer;
+template struct writer;
+template struct writer;
+template struct writer;
+template struct writer;
+
+namespace format_detail {
+
+template
+bool formatter::parse_next() {
+ format_parser parser{*static_cast *>(this)};
+ parser.read_and_append_up_to_next_specifier();
+ if (!*this->fmt) return false;
+ parser.parse_format_specifier();
+ return true;
+}
+
+template
+formatter::~formatter() {
+ // Write remaining formatting string
+ for (std::streamsize len = 0; ; ++len) {
+ if (!this->fmt[len]) {
+ this->out.write(this->fmt, len);
+ return;
+ }
+
+ if (this->fmt[len] == '%' && this->fmt[len + 1] == '%') {
+ this->out.write(this->fmt, len);
+ this->fmt += len + 1;
+ len = 0;
+ continue;
+ }
+ }
+}
+
+template class formatter;
+template class formatter;
+
+} }
+
diff --git a/libaegisub/include/libaegisub/format.h b/libaegisub/include/libaegisub/format.h
index 17781ae4c..6cdd8766c 100644
--- a/libaegisub/include/libaegisub/format.h
+++ b/libaegisub/include/libaegisub/format.h
@@ -18,7 +18,6 @@
#include
#include
-#include
#include
class wxString;
@@ -43,38 +42,6 @@ template
Out runtime_cast(In const& value) {
return runtime_cast_helper::cast(value);
}
-
-// Check length for string types
-template
-int actual_len(int max_len, const Char *value) {
- int len = 0;
- while (value[len] && (max_len <= 0 || len < max_len)) ++len;
- return len;
-}
-
-template
-int actual_len(int max_len, std::basic_string value) {
- if (max_len > 0 && static_cast(max_len) < value.size())
- return max_len;
- return value.size();
-}
-
-template
-inline void do_write_str(std::basic_ostream& out, const Char *str, int len) {
- out.write(str, len);
-}
-
-inline void do_write_str(std::ostream& out, const wchar_t *str, int len) {
- std::wstring_convert> utf8_conv;
- auto u8 = utf8_conv.to_bytes(str, str + len);
- out.write(u8.data(), u8.size());
-}
-
-inline void do_write_str(std::wostream& out, const char *str, int len) {
- std::wstring_convert> utf8_conv;
- auto wc = utf8_conv.from_bytes(str, str + len);
- out.write(wc.data(), wc.size());
-}
}
template
@@ -84,33 +51,27 @@ struct writer {
}
};
+template
+struct writer {
+ static void write(std::basic_ostream& out, int max_len, const Char *value);
+};
+
+template
+struct writer> {
+ static void write(std::basic_ostream& out, int max_len, std::basic_string const& value);
+};
+
// Ensure things with specializations don't get implicitly initialized
template<> struct writer;
template<> struct writer;
template<> struct writer;
template<> struct writer;
-template
-struct writer {
- static void write(std::basic_ostream& out, int max_len, const Char *value) {
- format_detail::do_write_str(out, value, format_detail::actual_len(max_len, value));
- }
-};
-
-template
-struct writer> {
- static void write(std::basic_ostream& out, int max_len, std::basic_string const& value) {
- format_detail::do_write_str(out, value.data(), format_detail::actual_len(max_len, value));
- }
-};
-
namespace format_detail {
template
-class formatter {
- formatter(const formatter&) = delete;
- formatter& operator=(const formatter&) = delete;
-
+struct formatter_state {
std::basic_ostream& out;
+
const Char *fmt;
const Char *fmt_cur = nullptr;
@@ -121,225 +82,101 @@ class formatter {
int width = 0;
int precision = 0;
+ formatter_state(std::basic_ostream&out , const Char *fmt)
+ : out(out), fmt(fmt) { }
+};
+
+template
+class formatter : formatter_state {
+ formatter(const formatter&) = delete;
+ formatter& operator=(const formatter&) = delete;
+
boost::io::basic_ios_all_saver saver;
- void read_and_append_up_to_next_specifier() {
- for (std::streamsize len = 0; ; ++len) {
- // Ran out of format specifiers; not an error due to that
- // translated strings may not need them all
- if (!fmt[len]) {
- out.write(fmt, len);
- fmt += len;
- return;
- }
-
- if (fmt[len] == '%') {
- if (fmt[len + 1] == '%') {
- out.write(fmt, len);
- fmt += len + 1;
- len = 0;
- continue;
- }
-
- out.write(fmt, len);
- fmt += len;
- break;
- }
- }
- }
-
- int read_int() {
- int i = 0;
- for (; *fmt_cur >= '0' && *fmt_cur <= '9'; ++fmt_cur)
- i = 10 * i + (*fmt_cur - '0');
- return i;
- }
-
- void parse_flags() {
- for (; ; ++fmt_cur) {
- switch (*fmt_cur) {
- // Not supported: ' ' (add a space before positive numers to align with negative)
- case '#':
- out.setf(std::ios::showpoint | std::ios::showbase);
- continue;
- case '0':
- // overridden by left alignment ('-' flag)
- if (!(out.flags() & std::ios::left)) {
- // Use internal padding so that numeric values are
- // formatted correctly, eg -00010 rather than 000-10
- out.fill('0');
- out.setf(std::ios::internal, std::ios::adjustfield);
- }
- continue;
- case '-':
- out.fill(' ');
- out.setf(std::ios::left, std::ios::adjustfield);
- continue;
- case '+':
- out.setf(std::ios::showpos);
- continue;
- }
- break;
- }
- }
-
- void parse_width() {
- if (*fmt_cur >= '0' && *fmt_cur <= '9')
- width = read_int();
- else if (*fmt_cur == '*') {
- read_width = true;
- pending = true;
- ++fmt_cur;
- }
- }
-
- void parse_precision() {
- if (*fmt_cur != '.') return;
- ++fmt_cur;
-
- // Ignoring negative precision because it's dumb and pointless
- if (*fmt_cur >= '0' && *fmt_cur <= '9')
- precision = read_int();
- else if (*fmt_cur == '*') {
- read_precision = true;
- pending = true;
- ++fmt_cur;
- }
- else
- precision = 0;
- }
-
- void parse_length_modifiers() {
- // Where "parse" means "skip" since we don't need them
- for (Char c = *fmt_cur;
- c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
- c = *++fmt_cur);
- }
-
- void parse_format_specifier() {
- width = 0;
- precision = -1;
- out.fill(' ');
- out.unsetf(
- std::ios::adjustfield |
- std::ios::basefield |
- std::ios::boolalpha |
- std::ios::floatfield |
- std::ios::showbase |
- std::ios::showpoint |
- std::ios::showpos |
- std::ios::uppercase);
-
- // Don't touch fmt until the specifier is fully applied so that if we
- // have insufficient arguments it'll get passed through to the output
- fmt_cur = fmt + 1;
-
- parse_flags();
- parse_width();
- parse_precision();
- parse_length_modifiers();
- }
+ bool parse_next();
public:
- formatter(std::basic_ostream& out, const Char *fmt) : out(out), fmt(fmt), saver(out) { }
- ~formatter() {
- // Write remaining formatting string
- for (std::streamsize len = 0; ; ++len) {
- if (!fmt[len]) {
- out.write(fmt, len);
- return;
- }
-
- if (fmt[len] == '%' && fmt[len + 1] == '%') {
- out.write(fmt, len);
- fmt += len + 1;
- len = 0;
- continue;
- }
- }
- }
+ formatter(std::basic_ostream& out, const Char *fmt)
+ : formatter_state(out, fmt), saver(out) { }
+ ~formatter();
template
void operator()(T&& value) {
- if (!pending) {
- read_and_append_up_to_next_specifier();
- if (!*fmt) return;
- parse_format_specifier();
- }
+ if (!this->pending && !parse_next()) return;
- if (read_width) {
- width = runtime_cast(value);
- read_width = false;
+ if (this->read_width) {
+ this->width = runtime_cast(value);
+ this->read_width = false;
return;
}
- if (read_precision) {
- precision = runtime_cast(value);
- read_precision = false;
+ if (this->read_precision) {
+ this->precision = runtime_cast(value);
+ this->read_precision = false;
return;
}
- pending = false;
+ this->pending = false;
- if (width < 0) {
- out.fill(' ');
- out.setf(std::ios::left, std::ios::adjustfield);
- width = -width;
+ if (this->width < 0) {
+ this->out.fill(' ');
+ this->out.setf(std::ios::left, std::ios::adjustfield);
+ this->width = -this->width;
}
- out.width(width);
- out.precision(precision < 0 ? 6 : precision);
+ this->out.width(this->width);
+ this->out.precision(this->precision < 0 ? 6 : this->precision);
- Char c = *fmt_cur ? fmt_cur[0] : 's';
+ Char c = *this->fmt_cur ? this->fmt_cur[0] : 's';
if (c >= 'A' && c <= 'Z') {
- out.setf(std::ios::uppercase);
+ this->out.setf(std::ios::uppercase);
c += 'a' - 'A';
}
switch (c) {
case 'c':
- out.setf(std::ios::dec, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::dec, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
case 'd': case 'i':
- out.setf(std::ios::dec, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::dec, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
case 'o':
- out.setf(std::ios::oct, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::oct, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
case 'x':
- out.setf(std::ios::hex, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::hex, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
case 'u':
- out.setf(std::ios::dec, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::dec, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
case 'e':
- out.setf(std::ios::scientific, std::ios::floatfield);
- out.setf(std::ios::dec, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::scientific, std::ios::floatfield);
+ this->out.setf(std::ios::dec, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
case 'f':
- out.setf(std::ios::fixed, std::ios::floatfield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::fixed, std::ios::floatfield);
+ this->out << runtime_cast(value);
break;
case 'g':
- out.setf(std::ios::dec, std::ios::basefield);
- out.flags(out.flags() & ~std::ios::floatfield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::dec, std::ios::basefield);
+ this->out.flags(this->out.flags() & ~std::ios::floatfield);
+ this->out << runtime_cast(value);
break;
case 'p':
- out.setf(std::ios::hex, std::ios::basefield);
- out << runtime_cast(value);
+ this->out.setf(std::ios::hex, std::ios::basefield);
+ this->out << runtime_cast(value);
break;
default: // s and other
- out.setf(std::ios::boolalpha);
- writer::type>::write(out, precision, value);
+ this->out.setf(std::ios::boolalpha);
+ writer::type>::write(this->out, this->precision, value);
break;
}
- fmt = *fmt_cur ? fmt_cur + 1 : fmt_cur;
+ this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur;
}
};
diff --git a/src/agi_pre.h b/src/agi_pre.h
index b31970552..7ac6dd3c8 100644
--- a/src/agi_pre.h
+++ b/src/agi_pre.h
@@ -72,7 +72,6 @@
// Common C++
#include
#include
-#include
#include
#include
#include
diff --git a/src/format.h b/src/format.h
index 381fb900c..c46ef10d4 100644
--- a/src/format.h
+++ b/src/format.h
@@ -23,16 +23,14 @@ namespace agi {
template<>
struct writer {
static void write(std::basic_ostream& out, int max_len, wxString const& value) {
- format_detail::do_write_str(out, value.wx_str(),
- format_detail::actual_len(max_len, value.wx_str()));
+ writer::write(out, max_len, value.wx_str());
}
};
template<>
struct writer {
static void write(std::basic_ostream& out, int max_len, wxString const& value) {
- format_detail::do_write_str(out, value.wx_str(),
- format_detail::actual_len(max_len, value.wx_str()));
+ writer::write(out, max_len, value.wx_str());
}
};