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