diff --git a/include/boost/beast2/format.hpp b/include/boost/beast2/format.hpp index 4c5c9e7..fb3994f 100644 --- a/include/boost/beast2/format.hpp +++ b/include/boost/beast2/format.hpp @@ -11,7 +11,9 @@ #define BOOST_BEAST2_FORMAT_HPP #include +#include #include + #include #include #include @@ -46,21 +48,59 @@ struct format_impl next() { has_placeholder = false; + bool unmatched_open = false; + bool unmatched_close = false; while (p != end) { - if (*p++ != '{') - continue; - if (p == end) - break; - if (*p++ == '}') + if(unmatched_open) + { + if(*p == '{') + { + p++; + core::string_view seg(p0, (p - 1) - p0); + p0 = p; + return seg; + } + if(*p == '}') + { + p++; + core::string_view seg(p0, (p - 2) - p0); + p0 = p; + has_placeholder = true; + return seg; + } + detail::throw_invalid_argument( + "invalid format string, unmatched {"); + } + if(unmatched_close) + { + if(*p == '}') + { + p++; + core::string_view seg(p0, (p - 1) - p0); + p0 = p; + return seg; + } + detail::throw_invalid_argument( + "invalid format string, unmatched }"); + } + if (*p == '{') + { + unmatched_open = true; + } + if(*p == '}') { - core::string_view seg( - p0, (p - 2) - p0); - p0 = p; - has_placeholder = true; - return seg; + unmatched_close = true; } + p++; } + if (unmatched_open) + detail::throw_invalid_argument( + "invalid format string, unmatched {"); + if(unmatched_close) + detail::throw_invalid_argument( + "invalid format string, unmatched }"); + core::string_view seg( p0, end - p0); p0 = end; @@ -71,10 +111,14 @@ struct format_impl void do_arg(Arg const& arg) { core::string_view seg = next(); - if (seg.size()) - os.write(seg.data(), static_cast< - std::streamsize>(seg.size())); - if (has_placeholder) + while(seg.size()) + { + os.write(seg.data(), static_cast(seg.size())); + if(has_placeholder) + break; + seg = next(); + } + if(has_placeholder) os << arg; }; @@ -83,9 +127,18 @@ struct format_impl { using expander = int[]; (void)expander{0, (do_arg(args), 0)...}; - if (p0 < end) - os.write(p0, static_cast< - std::streamsize>(end - p0)); + + core::string_view seg; + do + { + seg = next(); + if(has_placeholder) + detail::throw_invalid_argument( + "too few format arguments provided"); + if(seg.size()) + os.write(seg.data(), static_cast(seg.size())); + } + while(seg.size()); } }; diff --git a/test/unit/format.cpp b/test/unit/format.cpp index aabf66f..a4c506a 100644 --- a/test/unit/format.cpp +++ b/test/unit/format.cpp @@ -28,22 +28,49 @@ struct format_test BOOST_TEST_EQ(s, match); } + template + void e( + core::string_view match, + core::string_view fs, + Args const&... args) + { + BOOST_TEST_THROWS(f(match, fs, args...), std::invalid_argument); + } + void run() { + // Bad format strings, string arg. + e("{}", "{}"); + e("{", "{"); + e("}", "}"); + e("}{", "}{"); + e("{", "{", "x"); + e("}", "}", "x"); + e("}{", "}{", "x"); + e("{", "{", "x"); + e("}", "}", "x"); + e("}{", "}{", "x"); + e("1{}2{}3","1{}2{}3"); + e("1a2{}3", "1{}2{}3", "a"); + + // Good format strings, string arg. f("x", "x"); - f("{}", "{}"); - f("{", "{"); - f("}", "}"); - f("}{", "}{"); + f("{", "{{"); + f("}", "}}"); + f("}{", "}}{{"); f("x", "x"); f("x", "{}", "x"); - f("{", "{", "x"); - f("}", "}", "x"); - f("}{", "}{", "x"); - f("1{}2{}3","1{}2{}3"); - f("1a2{}3", "1{}2{}3", "a"); + f("x", "{}", "x"); + f("{", "{{", "x"); + f("}", "}}", "x"); + f("}{", "}}{{", "x"); f("1a2b3", "1{}2{}3", "a", "b"); + f("1a2b3", "1{}2{}3", "a", "b", "c"); f("hello world!", "hello {}!", "world"); + f("hello world! {} ", "hello {}! {{}} ", "world"); + + // Good format string, char arg + f("x", "{}", 'x'); } };