libfilezilla
format.hpp
Go to the documentation of this file.
1 #ifndef LIBFILEZILLA_FORMAT_HEADER
2 #define LIBFILEZILLA_FORMAT_HEADER
3 
4 #include "string.hpp"
5 
6 #include <cstdlib>
7 
8 #include <assert.h>
9 
14 namespace fz {
15 
17 namespace detail {
18 
19 // Get flags
20 enum : char {
21  pad_0 = 1,
22  pad_blank = 2,
23  with_width = 4,
24  left_align = 8,
25  always_sign = 16
26 };
27 
28 // Converts integral type to desired string type...
29 // ... basic case: simple unsigned value
30 template<typename String, bool Unsigned, typename Arg>
31 typename std::enable_if_t<std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_string(char flags, int width, Arg && arg)
32 {
33  std::decay_t<Arg> v = arg;
34 
35  char lead{};
36 
37  assert(!Unsigned || !std::is_signed<std::decay_t<Arg>>::value || arg >= 0);
38 
39  if (std::is_signed<std::decay_t<Arg>>::value && !(arg >= 0)) {
40  lead = '-';
41  }
42  else if (std::is_signed<std::decay_t<Arg>>::value && flags & always_sign) {
43  lead = '+';
44  }
45  else if (flags & pad_blank && arg >= 0) {
46  lead = ' ';
47  }
48 
49  // max decimal digits in b-bit integer is floor((b-1) * log_10(2)) + 1 < b * 0.5 + 1
50  typename String::value_type buf[sizeof(v) * 4 + 1];
51  auto *const end = buf + sizeof(v) * 4 + 1;
52  auto *p = end;
53 
54  do {
55  int const mod = std::abs(static_cast<int>(v % 10));
56  *(--p) = '0' + mod;
57  v /= 10;
58  } while (v);
59 
60  if (flags & with_width) {
61  if (lead && width > 0) {
62  --width;
63  }
64 
65  String ret;
66 
67  if (flags & pad_0) {
68  if (lead) {
69  ret += lead;
70  }
71  if (end - p < width) {
72  ret.append(width - (end - p), '0');
73  }
74  ret.append(p, end);
75  }
76  else {
77  if (end - p < width && !(flags & left_align)) {
78  ret.append(width - (end - p), ' ');
79  }
80  if (lead) {
81  ret += lead;
82  }
83  ret.append(p, end);
84  if (end - p < width && flags & left_align) {
85  ret.append(width - (end - p), ' ');
86  }
87  }
88 
89  return ret;
90  }
91  else {
92  if (lead) {
93  *(--p) = lead;
94  }
95  return String(p, end);
96  }
97 }
98 
99 // ... for strongly typed enums
100 template<typename String, bool Unsigned, typename Arg>
101 typename std::enable_if_t<std::is_enum<std::decay_t<Arg>>::value, String> integral_to_string(char flags, int width, Arg && arg)
102 {
103  return integral_to_string<String, Unsigned>(flags, width, static_cast<std::underlying_type_t<std::decay_t<Arg>>>(arg));
104 }
105 
106 // ... assert otherwise
107 template<typename String, bool Unsigned, typename Arg>
108 typename std::enable_if_t<!std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_string(char, int, Arg &&)
109 {
110  assert(0);
111  return String();
112 }
113 
114 
115 // Converts argument to string...
116 // ... if toString(arg) is valid expression
117 template<typename String, typename Arg>
118 auto arg_to_string(Arg&& arg) -> decltype(toString<String>(std::forward<Arg>(arg)))
119 {
120  return toString<String>(std::forward<Arg>(arg));
121 }
122 
123 // ... assert otherwise
124 template<typename String>
125 String arg_to_string(...)
126 {
127  assert(0);
128  return String();
129 }
130 
131 
132 // Converts integral type to hex string with desired string type...
133 // ... basic case: simple unsigned value
134 template<typename String, bool Lowercase, typename Arg>
135 typename std::enable_if_t<std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_hex_string(Arg && arg)
136 {
137  std::decay_t<Arg> v = arg;
138  typename String::value_type buf[sizeof(v) * 2];
139  auto *const end = buf + sizeof(v) * 2;
140  auto *p = end;
141 
142  do {
143  *(--p) = fz::int_to_hex_char<typename String::value_type, Lowercase>(v & 0xf);
144  v >>= 4;
145  } while (v);
146 
147  return String(p, end);
148 }
149 
150 // ... for enums
151 template<typename String, bool Lowercase, typename Arg>
152 typename std::enable_if_t<std::is_enum<std::decay_t<Arg>>::value, String> integral_to_hex_string(Arg && arg)
153 {
154  return integral_to_hex_string<String, Lowercase>(static_cast<std::underlying_type_t<std::decay_t<Arg>>>(arg));
155 }
156 
157 // ... assert otherwise
158 template<typename String, bool Lowercase, typename Arg>
159 typename std::enable_if_t<!std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_hex_string(Arg &&)
160 {
161  assert(0);
162  return String();
163 }
164 
165 
166 // Converts to pointer to hex string
167 template<typename String, typename Arg>
168 typename std::enable_if_t<std::is_pointer<std::decay_t<Arg>>::value, String> pointer_to_string(Arg&& arg)
169 {
170  return String({'0', 'x'}) + integral_to_hex_string<String, true>(reinterpret_cast<uintptr_t>(arg));
171 }
172 
173 
174 template<typename String, typename Arg>
175 typename std::enable_if_t<!std::is_pointer<std::decay_t<Arg>>::value, String> pointer_to_string(Arg&&)
176 {
177  assert(0);
178  return String();
179 }
180 
181 
182 template<typename String, typename... Args>
183 String extract_arg(char, size_t, typename String::value_type, size_t)
184 {
185  return String();
186 }
187 
188 template<typename String>
189 void pad_arg(String& s, char flags, size_t width)
190 {
191  if (flags & with_width && s.size() < width) {
192  if (flags & left_align) {
193  s += String(width - s.size(), ' ');
194  }
195  else {
196  s = String(width - s.size(), (flags & pad_0) ? '0' : ' ') + s;
197  }
198  }
199 }
200 
201 template<typename String, typename Arg, typename... Args>
202 String extract_arg(char flags, size_t width, typename String::value_type type, size_t arg_n, Arg&& arg, Args&&...args)
203 {
204  String ret;
205 
206  if (!arg_n) {
207  if (type == 's') {
208  ret = arg_to_string<String>(std::forward<Arg>(arg));
209  pad_arg(ret, flags, width);
210  }
211  else if (type == 'd' || type == 'i') {
212  ret = integral_to_string<String, false>(flags, width, std::forward<Arg>(arg));
213  }
214  else if (type == 'u') {
215  ret = integral_to_string<String, true>(flags, width, std::forward<Arg>(arg));
216  }
217  else if (type == 'x') {
218  ret = integral_to_hex_string<String, true>(std::forward<Arg>(arg));
219  pad_arg(ret, flags, width);
220  }
221  else if (type == 'X') {
222  ret = integral_to_hex_string<String, false>(std::forward<Arg>(arg));
223  pad_arg(ret, flags, width);
224  }
225  else if (type == 'p') {
226  ret = pointer_to_string<String>(std::forward<Arg>(arg));
227  pad_arg(ret, flags, width);
228  }
229  else {
230  assert(0);
231  }
232  }
233  else {
234  ret = extract_arg<String>(flags, width, type, arg_n - 1, std::forward<Args>(args)...);
235  }
236 
237  return ret;
238 }
239 
240 template<typename String, typename... Args>
241 void process_arg(String const& fmt, typename String::size_type & pos, String& ret, size_t& arg_n, Args&&... args)
242 {
243  ++pos;
244 
245  // Get literal percent out of the way
246  if (fmt[pos] == '%') {
247  ret += '%';
248  ++pos;
249  return;
250  }
251 
252 parse_start:
253  char flags{};
254  while (true) {
255  if (fmt[pos] == '0') {
256  flags |= pad_0;
257  }
258  else if (fmt[pos] == ' ') {
259  flags |= pad_blank;
260  }
261  else if (fmt[pos] == '-') {
262  flags &= ~pad_0;
263  flags |= left_align;
264  }
265  else if (fmt[pos] == '+') {
266  flags &= ~pad_blank;
267  flags |= always_sign;
268  }
269  else {
270  break;
271  }
272  ++pos;
273  }
274 
275  // Field width
276  size_t width{};
277  while (fmt[pos] >= '0' && fmt[pos] <= '9') {
278  flags |= with_width;
279  width *= 10;
280  width += fmt[pos] - '0';
281  ++pos;
282  }
283  if (width > 10000) {
284  assert(0);
285  width = 10000;
286  }
287 
288  if (fmt[pos] == '$') {
289  // Positional argument, start over
290  arg_n = width - 1;
291  ++pos;
292  goto parse_start;
293  }
294 
295  // Ignore length modifier
296  while (true) {
297  auto c = fmt[pos];
298  if (c == 'h' || c == 'l' || c == 'L' || c == 'j' || c == 'z' || c == 't') {
299  ++pos;
300  }
301  else {
302  break;
303  }
304  }
305 
306  assert(arg_n < sizeof...(args));
307  if (arg_n >= sizeof...(args)) {
308  ++pos;
309  return;
310  }
311 
312  auto const type = fmt[pos++];
313 
314  ret += extract_arg<String>(flags, width, type, arg_n++, std::forward<Args>(args)...);
315 
316  // Now we're ready to print!
317 }
318 
319 }
321 
343 template<typename String, typename... Args>
344 String sprintf(String const& fmt, Args&&... args)
345 {
346  String ret;
347 
348  // Find % characters
349  typename String::size_type start = 0, pos;
350  size_t arg_n{};
351  while ((pos = fmt.find('%', start)) != String::npos) {
352 
353  // Copy segment preceeding the %
354  ret += fmt.substr(start, pos - start);
355 
356  detail::process_arg(fmt, pos, ret, arg_n, std::forward<Args>(args)...);
357 
358  start = pos;
359  }
360 
361  // Copy remainder of string
362  ret += fmt.substr(start);
363 
364  return ret;
365 }
366 
367 template<typename... Args>
368 std::string sprintf(char const* fmt, Args&&... args)
369 {
370  return sprintf(std::string(fmt), std::forward<Args>(args)...);
371 }
372 
373 template<typename... Args>
374 std::wstring sprintf(wchar_t const* fmt, Args&&... args)
375 {
376  return sprintf(std::wstring(fmt), std::forward<Args>(args)...);
377 }
378 
379 }
380 
381 #endif
String sprintf(String const &fmt, Args &&... args)
A simple type-safe sprintf replacement.
Definition: format.hpp:344
String types and assorted functions.
The namespace used by libfilezilla.
Definition: apply.hpp:16