Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/beast2
8 : //
9 :
10 : #ifndef BOOST_BEAST2_FORMAT_HPP
11 : #define BOOST_BEAST2_FORMAT_HPP
12 :
13 : #include <boost/beast2/detail/config.hpp>
14 : #include <boost/beast2/detail/except.hpp>
15 : #include <boost/core/detail/string_view.hpp>
16 :
17 : #include <memory>
18 : #include <ostream>
19 : #include <streambuf>
20 : #include <string>
21 :
22 : namespace boost {
23 : namespace beast2 {
24 :
25 : namespace detail {
26 :
27 : struct format_impl
28 : {
29 : std::ostream& os;
30 : core::string_view fs;
31 : char const* p;
32 : char const* p0;
33 : char const* end;
34 : bool has_placeholder = false;
35 :
36 27 : format_impl(
37 : std::ostream& os_,
38 : core::string_view fs_)
39 27 : : os(os_)
40 27 : , fs(fs_)
41 27 : , p(fs.data())
42 27 : , p0(p)
43 27 : , end(p + fs.size())
44 : {
45 27 : }
46 :
47 : core::string_view
48 57 : next()
49 : {
50 57 : has_placeholder = false;
51 57 : bool unmatched_open = false;
52 57 : bool unmatched_close = false;
53 116 : while (p != end)
54 : {
55 85 : if(unmatched_open)
56 : {
57 18 : if(*p == '{')
58 : {
59 5 : p++;
60 5 : core::string_view seg(p0, (p - 1) - p0);
61 5 : p0 = p;
62 5 : return seg;
63 : }
64 13 : if(*p == '}')
65 : {
66 13 : p++;
67 13 : core::string_view seg(p0, (p - 2) - p0);
68 13 : p0 = p;
69 13 : has_placeholder = true;
70 13 : return seg;
71 : }
72 0 : detail::throw_invalid_argument(
73 : "invalid format string, unmatched {");
74 : }
75 67 : if(unmatched_close)
76 : {
77 8 : if(*p == '}')
78 : {
79 5 : p++;
80 5 : core::string_view seg(p0, (p - 1) - p0);
81 5 : p0 = p;
82 5 : return seg;
83 : }
84 3 : detail::throw_invalid_argument(
85 : "invalid format string, unmatched }");
86 : }
87 59 : if (*p == '{')
88 : {
89 21 : unmatched_open = true;
90 : }
91 59 : if(*p == '}')
92 : {
93 11 : unmatched_close = true;
94 : }
95 59 : p++;
96 : }
97 31 : if (unmatched_open)
98 3 : detail::throw_invalid_argument(
99 : "invalid format string, unmatched {");
100 28 : if(unmatched_close)
101 3 : detail::throw_invalid_argument(
102 : "invalid format string, unmatched }");
103 :
104 : core::string_view seg(
105 25 : p0, end - p0);
106 25 : p0 = end;
107 25 : return seg;
108 : }
109 :
110 : template<class Arg>
111 20 : void do_arg(Arg const& arg)
112 : {
113 20 : core::string_view seg = next();
114 19 : while(seg.size())
115 : {
116 12 : os.write(seg.data(), static_cast<std::streamsize>(seg.size()));
117 12 : if(has_placeholder)
118 7 : break;
119 5 : seg = next();
120 : }
121 14 : if(has_placeholder)
122 10 : os << arg;
123 14 : };
124 :
125 : template<class... Args>
126 27 : void operator()(Args const&... args)
127 : {
128 : using expander = int[];
129 27 : (void)expander{0, (do_arg(args), 0)...};
130 :
131 21 : core::string_view seg;
132 : do
133 : {
134 32 : seg = next();
135 29 : if(has_placeholder)
136 3 : detail::throw_invalid_argument(
137 : "too few format arguments provided");
138 26 : if(seg.size())
139 11 : os.write(seg.data(), static_cast<std::streamsize>(seg.size()));
140 : }
141 26 : while(seg.size());
142 15 : }
143 : };
144 :
145 : class appendbuf : public std::streambuf
146 : {
147 : std::string* s_;
148 :
149 : protected:
150 : // Called when a single character is to be written
151 1 : virtual int_type overflow(int_type ch) override
152 : {
153 1 : if (ch != traits_type::eof())
154 : {
155 1 : s_->push_back(static_cast<char>(ch));
156 1 : return ch;
157 : }
158 0 : return traits_type::eof();
159 : }
160 :
161 : // Called when multiple characters are to be written
162 32 : virtual std::streamsize xsputn(const char* s, std::streamsize n) override
163 : {
164 32 : s_->append(s, static_cast<std::size_t>(n));
165 32 : return n;
166 : }
167 :
168 : public:
169 27 : explicit appendbuf(std::string& s)
170 27 : : s_(&s)
171 : {
172 27 : }
173 : };
174 :
175 : class appendstream : public std::ostream
176 : {
177 : appendbuf buf_;
178 :
179 : public:
180 27 : explicit appendstream(std::string& s)
181 27 : : std::ostream(&buf_)
182 27 : , buf_(s)
183 : {
184 27 : }
185 : };
186 :
187 : } // detail
188 :
189 : /** Format arguments using a format string
190 : */
191 : template<class... Args>
192 : void
193 27 : format_to(
194 : std::ostream& os,
195 : core::string_view fs,
196 : Args const&... args)
197 : {
198 27 : detail::format_impl(os, fs)(args...);
199 15 : }
200 :
201 : /** Format arguments using a format string
202 : */
203 : template<class... Args>
204 : void
205 27 : format_to(
206 : std::string& dest,
207 : core::string_view fs,
208 : Args const&... args)
209 : {
210 27 : detail::appendstream ss(dest);
211 27 : format_to(ss, fs, args...);
212 27 : }
213 :
214 : } // beast2
215 : } // boost
216 :
217 : #endif
|