Guitar
strformat.h
Go to the documentation of this file.
1 // String Formatter
2 // Copyright (C) 2025 S.Fuchita (soramimi_jp)
3 // This software is distributed under the MIT license.
4 
5 #ifndef STRFORMAT_H
6 #define STRFORMAT_H
7 
8 // #define STRFORMAT_NO_LOCALE
9 // #define STRFORMAT_NO_FP
10 
11 #include <algorithm>
12 #include <cmath>
13 #include <cstdint>
14 #include <cstdio>
15 #include <cstdlib>
16 #include <cstring>
17 #include <functional>
18 #include <string>
19 #include <vector>
20 #include <string_view>
21 
22 #ifndef STRFORMAT_NO_LOCALE
23 #include <locale.h>
24 #endif
25 
26 #ifdef _MSC_VER
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31 
32 namespace strformat_ns {
33 
34 class misc {
35 private:
46  static double pow10_int(int exp)
47  {
48  // Pre‑computed powers for |exp| ≤ 16
49  static const double tbl[] = {
50  1e+00, 1e+01, 1e+02, 1e+03, 1e+04, 1e+05, 1e+06,
51  1e+07, 1e+08, 1e+09, 1e+10, 1e+11, 1e+12, 1e+13,
52  1e+14, 1e+15, 1e+16
53  };
54  if (exp >= 0 && exp < static_cast<int>(sizeof tbl / sizeof *tbl))
55  return tbl[exp];
56  if (exp <= 0 && exp > -static_cast<int>(sizeof tbl / sizeof *tbl))
57  return 1.0 / tbl[-exp];
58  // Rare case: delegate to libm
59  return std::pow(10.0, exp);
60  }
61 public:
77  static double my_strtod(const char *nptr, char **endptr)
78  {
79  const char *s = nptr;
80  bool sign = false;
81  bool saw_digit = false;
82  int frac_digits = 0;
83  long exp_val = 0;
84  bool exp_sign = false;
85  double value = 0.0;
86 
87  // Skip leading white‑space
88  while (std::isspace((unsigned char)*s)) ++s;
89 
90  // Parse optional sign
91  if (*s == '+' || *s == '-') {
92  if (*s == '-') sign = true;
93  s++;
94  }
95 
96  // Integer part
97  while (std::isdigit((unsigned char)*s)) {
98  saw_digit = true;
99  value = value * 10.0 + (*s - '0');
100  s++;
101  }
102 
103  // Fractional part
104  if (*s == '.') {
105  s++;
106  while (std::isdigit((unsigned char)*s)) {
107  saw_digit = true;
108  value = value * 10.0 + (*s - '0');
109  s++;
110  frac_digits++;
111  }
112  }
113 
114  // No digits at all -> conversion failure
115  if (!saw_digit) {
116  if (endptr) *endptr = const_cast<char *>(nptr);
117  return 0.0;
118  }
119 
120  // Exponent part
121  if (*s == 'e' || *s == 'E') {
122  s++;
123  const char *exp_start = s;
124  if (*s == '+' || *s == '-') {
125  if (*s == '-') exp_sign = true;
126  s++;
127  }
128  if (std::isdigit((unsigned char)*s)) {
129  while (std::isdigit((unsigned char)*s)) {
130  exp_val = exp_val * 10 + (*s - '0');
131  s++;
132  }
133  if (exp_sign) {
134  exp_val = -exp_val;
135  }
136  } else {
137  // Roll back if 'e' is not followed by a valid exponent
138  s = exp_start - 1;
139  }
140  }
141 
142  // Scale by 10^(exponent − #fractional‑digits)
143  int total_exp = exp_val - frac_digits;
144  if (total_exp != 0) {
145  value *= pow10_int(total_exp);
146  }
147 
148  // Apply sign
149  if (sign) {
150  value = -value;
151  }
152 
153  // Set errno on overflow/underflow
154  if (!std::isfinite(value)) {
155  // errno = ERANGE;
156  value = sign ? -HUGE_VAL : HUGE_VAL;
157  } else if (value == 0.0 && saw_digit && total_exp != 0) {
158  // errno = ERANGE; // underflow
159  }
160 
161  // Report where parsing stopped
162  if (endptr) *endptr = const_cast<char *>(s);
163  return value;
164  }
165 };
166 
167 struct NumberParser {
168  char const *p;
169  bool sign = false;
170  int radix = 10;
171  NumberParser(char const *ptr)
172  : p(ptr)
173  {
174  while (isspace((unsigned char)*p)) {
175  p++;
176  }
177  if (*p == '+') {
178  p++;
179  } else if (*p == '-') {
180  sign = true;
181  p++;
182  }
183  if (p[0] == '0') {
184  if (p[1] == 'x' || p[1] == 'X') {
185  p += 2;
186  radix = 16;
187  } else {
188  int i = 1;
189  while (p[i]) {
190  int c = (unsigned char)p[i];
191  if (c < '0' || c > '7') break;
192  radix = 8;
193  i++;
194  }
195  }
196  }
197  }
198 };
199 
200 template <typename T> static inline T parse_number(char const *ptr, std::function<T(char const *p, int radix)> conv)
201 {
202  NumberParser t(ptr);
203  T v = conv(t.p, t.radix);
204  if (t.sign) v = -v;
205  return v;
206 }
207 
208 struct Option_ {
209 #ifdef STRFORMAT_NO_LOCALE
210  void *lc = nullptr;
211 #else
212  struct lconv *lc = nullptr;
213 #endif
214 };
215 
216 template <typename T> static inline T num(char const *value, Option_ const &opt);
217 template <> inline char num<char>(char const *value, Option_ const &opt)
218 {
219  return parse_number<char>(value, [](char const *p, int radix){
220  return (char)strtol(p, nullptr, radix);
221  });
222 }
223 template <> inline int32_t num<int32_t>(char const *value, Option_ const &opt)
224 {
225  return parse_number<int32_t>(value, [](char const *p, int radix){
226  return strtol(p, nullptr, radix);
227  });
228 }
229 template <> inline uint32_t num<uint32_t>(char const *value, Option_ const &opt)
230 {
231  return parse_number<uint32_t>(value, [](char const *p, int radix){
232  return strtoul(p, nullptr, radix);
233  });
234 }
235 template <> inline int64_t num<int64_t>(char const *value, Option_ const &opt)
236 {
237  return parse_number<int64_t>(value, [](char const *p, int radix){
238  return strtoll(p, nullptr, radix);
239  });
240 }
241 template <> inline uint64_t num<uint64_t>(char const *value, Option_ const &opt)
242 {
243  return parse_number<uint64_t>(value, [](char const *p, int radix){
244  return strtoull(p, nullptr, radix);
245  });
246 }
247 #ifndef STRFORMAT_NO_FP
248 template <> inline double num<double>(char const *value, Option_ const &opt)
249 {
250  return parse_number<double>(value, [&opt](char const *p, int radix){
251  if (radix == 10) {
252  if (opt.lc) {
253  // locale-dependent
254  return strtod(p, nullptr);
255  } else {
256  // locale-independent
257  return misc::my_strtod(p, nullptr);
258  }
259  } else {
260  return (double)strtoll(p, nullptr, radix);
261  }
262  });
263 }
264 #endif
265 template <typename T> static inline T num(std::string const &value, Option_ const &opt)
266 {
267  return num<T>(value.data(), opt);
268 }
269 
271 public:
272  enum Flags {
273  Locale = 0x0001,
274  };
275 private:
276  struct Part {
278  int size;
279  char data[1];
280  };
281  struct PartList {
282  Part *head = nullptr;
283  Part *last = nullptr;
284  };
285  static Part *alloc_part(const char *data, int size)
286  {
287  Part *p = (Part *)new char[sizeof(Part) + size];
288  p->next = nullptr;
289  p->size = size;
290  memcpy(p->data, data, size);
291  p->data[size] = 0;
292  return p;
293  }
294  static Part *alloc_part(const char *begin, const char *end)
295  {
296  return alloc_part(begin, end - begin);
297  }
298  static Part *alloc_part(const char *str)
299  {
300  return alloc_part(str, strlen(str));
301  }
302  static Part *alloc_part(const std::string_view &str)
303  {
304  return alloc_part(str.data(), (int)str.size());
305  }
306  static void free_part(Part **p)
307  {
308  if (p && *p) {
309  delete[] *p;
310  *p = nullptr;
311  }
312  }
313  static void add_part(PartList *list, Part *part)
314  {
315  if (part) {
316  if (!list->head) {
317  list->head = part;
318  }
319  if (list->last) {
320  list->last->next = part;
321  }
322  list->last = part;
323  }
324  }
325  static void free_list(PartList *list)
326  {
327  Part *p = list->head;
328  while (p) {
329  Part *next = p->next;
330  free_part(&p);
331  p = next;
332  }
333  list->head = nullptr;
334  list->last = nullptr;
335  }
336  static void add_chars(PartList *list, char c, int n)
337  {
338  Part *p = (Part *)new char[sizeof(Part) + n];
339  p->next = nullptr;
340  p->size = n;
341  memset(p->data, c, n);
342  p->data[n] = 0;
343  add_part(list, p);
344  }
345  //
346  static char const *digits_lower()
347  {
348  return "0123456789abcdef";
349  }
350  static char const *digits_upper()
351  {
352  return "0123456789ABCDEF";
353  }
354  //
355 #ifndef STRFORMAT_NO_FP
356  Part *format_double(double val, int precision, bool trim_zeros, bool plus)
357  {
358  if (std::isnan(val)) return alloc_part("#NAN");
359  if (std::isinf(val)) return alloc_part("#INF");
360 
361  char *ptr, *end;
362 
363  char *dot = nullptr;
364 
365  bool sign = val < 0;
366  if (sign) {
367  val = -val;
368  }
369 
370  double intval = floor(val);
371  val -= intval;
372 
373  int intlen = 0;
374  if (intval == 0) {
375  ptr = end = (char *)alloca(precision + 10) + 5;
376  } else {
377  double t = intval;
378  do {
379  t = floor(t / 10);
380  intlen++;
381  } while (t != 0);
382  ptr = end = (char *)alloca(intlen + precision + 10) + intlen + 5;
383  }
384 
385  if (precision > 0) {
386  dot = end;
387  *end++ = decimal_point();
388  double v = val;
389  int e = 0;
390  while (v > 0 && v < 1) {
391  v *= 10;
392  e++;
393  }
394  while (v >= 1) {
395  v /= 10;
396  e--;
397  }
398  double add = 0.5;
399  for (int i = 0; i < precision - e; i++) {
400  add /= 10;
401  }
402  v += add;
403  double t = floor(v);
404  intval += t;
405  v -= t;
406  int i = 0;
407  int n = intlen;
408  int r = std::min(e, precision);
409  while (i < r) {
410  *end++ = '0';
411  if (n != 0) {
412  n++;
413  }
414  i++;
415  }
416  while (i < precision) {
417  if (n < 16) {
418  v *= 10;
419  double m = floor(v);
420  v -= m;
421  *end++ = (char)m + '0';
422  } else {
423  *end++ = '0';
424  }
425  n++;
426  i++;
427  }
428  } else {
429  intval += floor(val + 0.5);
430  }
431 
432  intlen = 0;
433  double t = intval;
434  do {
435  t = floor(t / 10);
436  intlen++;
437  } while (t != 0);
438 
439  if (intval == 0) {
440  *--ptr = '0';
441  } else {
442  double t = intval;
443  for (int i = 0; i < intlen; i++) {
444  t /= 10;
445  double u = floor(t);
446  *--ptr = (char)((t - u) * 10 + 0.49) + '0';
447  t = u;
448  }
449  }
450 
451  if (sign) {
452  *--ptr = '-';
453  } else if (plus) {
454  *--ptr = '+';
455  }
456 
457  if (trim_zeros && dot) {
458  while (dot < end) {
459  char c = end[-1];
460  if (c == '.') {
461  end--;
462  break;
463  }
464  if (c != '0') {
465  break;
466  }
467  end--;
468  }
469  }
470 
471  return alloc_part(ptr, end);
472  }
473 #endif
474  static Part *format_int32(int32_t val, bool force_sign)
475  {
476  int n = 30;
477  char *end = (char *)alloca(n) + n - 1;
478  char *ptr = end;
479  *end = 0;
480 
481  if (val == 0) {
482  *--ptr = '0';
483  } else {
484  bool sign = (val < 0);
485  uint32_t v;
486  if (sign) {
487  v = (uint32_t)-val;
488  } else {
489  v = (uint32_t)val;;
490  }
491  while (v != 0) {
492  int c = v % 10 + '0';
493  v /= 10;
494  *--ptr = c;
495  }
496  if (sign) {
497  *--ptr = '-';
498  } else if (force_sign) {
499  *--ptr = '+';
500  }
501  }
502 
503  return alloc_part(ptr, end);
504  }
505  static Part *format_uint32(uint32_t val)
506  {
507  int n = 30;
508  char *end = (char *)alloca(n) + n - 1;
509  char *ptr = end;
510  *end = 0;
511 
512  if (val == 0) {
513  *--ptr = '0';
514  } else {
515  while (val != 0) {
516  int c = val % 10 + '0';
517  val /= 10;
518  *--ptr = c;
519  }
520  }
521 
522  return alloc_part(ptr, end);
523  }
524  static Part *format_int64(int64_t val, bool force_sign)
525  {
526  int n = 30;
527  char *end = (char *)alloca(n) + n - 1;
528  char *ptr = end;
529  *end = 0;
530 
531  if (val == 0) {
532  *--ptr = '0';
533  } else {
534  if (val == (int64_t)1 << 63) {
535  *--ptr = '8';
536  val /= 10;
537  }
538  bool sign = (val < 0);
539  if (sign) val = -val;
540 
541  while (val != 0) {
542  int c = val % 10 + '0';
543  val /= 10;
544  *--ptr = c;
545  }
546  if (sign) {
547  *--ptr = '-';
548  } else if (force_sign) {
549  *--ptr = '+';
550  }
551  }
552 
553  return alloc_part(ptr, end);
554  }
555  static Part *format_uint64(uint64_t val)
556  {
557  int n = 30;
558  char *end = (char *)alloca(n) + n - 1;
559  char *ptr = end;
560  *end = 0;
561 
562  if (val == 0) {
563  *--ptr = '0';
564  } else {
565  while (val != 0) {
566  int c = val % 10 + '0';
567  val /= 10;
568  *--ptr = c;
569  }
570  }
571 
572  return alloc_part(ptr, end);
573  }
574  static Part *format_oct32(uint32_t val)
575  {
576  int n = 30;
577  char *end = (char *)alloca(n) + n - 1;
578  char *ptr = end;
579  *end = 0;
580 
581  char const *digits = digits_lower();
582 
583  if (val == 0) {
584  *--ptr = '0';
585  } else {
586  while (val != 0) {
587  char c = digits[val & 7];
588  val >>= 3;
589  *--ptr = c;
590  }
591  }
592 
593  return alloc_part(ptr, end);
594  }
595  static Part *format_oct64(uint64_t val)
596  {
597  int n = 30;
598  char *end = (char *)alloca(n) + n - 1;
599  char *ptr = end;
600  *end = 0;
601 
602  char const *digits = digits_lower();
603 
604  if (val == 0) {
605  *--ptr = '0';
606  } else {
607  while (val != 0) {
608  char c = digits[val & 7];
609  val >>= 3;
610  *--ptr = c;
611  }
612  }
613 
614  return alloc_part(ptr, end);
615  }
616  static Part *format_hex32(uint32_t val, bool upper)
617  {
618  int n = 30;
619  char *end = (char *)alloca(n) + n - 1;
620  char *ptr = end;
621  *end = 0;
622 
623  char const *digits = upper ? digits_upper() : digits_lower();
624 
625  if (val == 0) {
626  *--ptr = '0';
627  } else {
628  while (val != 0) {
629  char c = digits[val & 15];
630  val >>= 4;
631  *--ptr = c;
632  }
633  }
634 
635  return alloc_part(ptr, end);
636  }
637  static Part *format_hex64(uint64_t val, bool upper)
638  {
639  int n = 30;
640  char *end = (char *)alloca(n) + n - 1;
641  char *ptr = end;
642  *end = 0;
643 
644  char const *digits = upper ? digits_upper() : digits_lower();
645 
646  if (val == 0) {
647  *--ptr = '0';
648  } else {
649  while (val != 0) {
650  char c = digits[val & 15];
651  val >>= 4;
652  *--ptr = c;
653  }
654  }
655 
656  return alloc_part(ptr, end);
657  }
658  static Part *format_pointer(void *val)
659  {
660  int n = sizeof(uintptr_t) * 2 + 1;
661  char *end = (char *)alloca(n) + n - 1;
662  char *ptr = end;
663  *end = 0;
664 
665  char const *digits = digits_upper();
666 
667  uintptr_t v = (uintptr_t)val;
668  for (int i = 0; i < (int)sizeof(uintptr_t) * 2; i++) {
669  char c = digits[v & 15];
670  v >>= 4;
671  *--ptr = c;
672  }
673 
674  return alloc_part(ptr, end);
675  }
676 private:
677  struct Private {
678  std::string text;
679  char const *head;
680  char const *next;
682  bool upper : 1;
683  bool zero_padding : 1;
684  bool align_left : 1;
685  bool plus : 1;
686  int width;
688  int lflag;
690  } q;
691 
692  void _init()
693  {
694  q.list = {};
695  }
696 
697  void clear()
698  {
699  free_list(&q.list);
700  }
701  bool advance(bool complete)
702  {
703  bool r = false;
704  auto Flush = [&](){
705  if (q.head < q.next) {
706  Part *p = alloc_part(q.head, q.next);
707  add_part(&q.list, p);
708  q.head = q.next;
709  }
710  };
711  while (*q.next) {
712  if (*q.next == '%') {
713  if (q.next[1] == '%') {
714  q.next++;
715  Flush();
716  q.next++;
717  q.head = q.next;
718  } else if (complete) {
719  q.next++;
720  } else {
721  r = true;
722  break;
723  }
724  } else {
725  q.next++;
726  }
727  }
728  Flush();
729  return r;
730  }
731 #ifndef STRFORMAT_NO_FP
732  Part *format_f(double value, bool trim_zeros)
733  {
734  int pr = q.precision < 0 ? 6 : q.precision;
735  return format_double(value, pr, trim_zeros, q.plus);
736  }
737 #endif
738  Part *format_c(char c)
739  {
740  return alloc_part(&c, &c + 1);
741  }
742  Part *format_o32(uint32_t value, int hint)
743  {
744  if (hint) {
745  switch (hint) {
746  case 'c': return format_c((char)value);
747  case 'd': return format((int32_t)value, 0);
748  case 'u': return format(value, 0);
749  case 'x': return format_x32(value, 0);
750 #ifndef STRFORMAT_NO_FP
751  case 'f': return format((double)value, 0);
752 #endif
753  }
754  }
755  return format_oct32(value);
756  }
757  Part *format_o64(uint64_t value, int hint)
758  {
759  if (hint) {
760  switch (hint) {
761  case 'c': return format_c((char)value);
762  case 'd': return format((int64_t)value, 0);
763  case 'u': return format(value, 0);
764  case 'x': return format_x64(value, 0);
765 #ifndef STRFORMAT_NO_FP
766  case 'f': return format((double)value, 0);
767 #endif
768  }
769  }
770  return format_oct64(value);
771  }
772  Part *format_x32(uint32_t value, int hint)
773  {
774  if (hint) {
775  switch (hint) {
776  case 'c': return format_c((char)value);
777  case 'd': return format((int32_t)value, 0);
778  case 'u': return format(value, 0);
779  case 'o': return format_o32(value, 0);
780 #ifndef STRFORMAT_NO_FP
781  case 'f': return format((double)value, 0);
782 #endif
783  }
784  }
785  return format_hex32(value, q.upper);
786  }
787  Part *format_x64(uint64_t value, int hint)
788  {
789  if (hint) {
790  switch (hint) {
791  case 'c': return format_c((char)value);
792  case 'd': return format((int64_t)value, 0);
793  case 'u': return format(value, 0);
794  case 'o': return format_o64(value, 0);
795 #ifndef STRFORMAT_NO_FP
796  case 'f': return format((double)value, 0);
797 #endif
798  }
799  }
800  return format_hex64(value, q.upper);
801  }
802  Part *format(char c, int hint)
803  {
804  return format((int32_t)c, hint);
805  }
806 #ifndef STRFORMAT_NO_FP
807  Part *format(double value, int hint)
808  {
809  if (hint) {
810  switch (hint) {
811  case 'c': return format_c((char)value);
812  case 'd': return format((int64_t)value, 0);
813  case 'u': return format((uint64_t)value, 0);
814  case 'o': return format_o64((uint64_t)value, 0);
815  case 'x': return format_x64((uint64_t)value, 0);
816  case 's': return format_f(value, true);
817  }
818  }
819  return format_f(value, false);
820  }
821 #endif
822  Part *format(int32_t value, int hint)
823  {
824  if (hint) {
825  switch (hint) {
826  case 'c': return format_c((char)value);
827  case 'u': return format((uint32_t)value, 0);
828  case 'o': return format_o32((uint32_t)value, 0);
829  case 'x': return format_x32((uint32_t)value, 0);
830 #ifndef STRFORMAT_NO_FP
831  case 'f': return format((double)value, 0);
832 #endif
833  }
834  }
835  return format_int32(value, q.plus);
836  }
837  Part *format(uint32_t value, int hint)
838  {
839  if (hint) {
840  switch (hint) {
841  case 'c': return format_c((char)value);
842  case 'd': return format((int32_t)value, 0);
843  case 'o': return format_o32((uint32_t)value, 0);
844  case 'x': return format_x32((uint32_t)value, 0);
845 #ifndef STRFORMAT_NO_FP
846  case 'f': return format((double)value, 0);
847 #endif
848  }
849  }
850  return format_uint32(value);
851  }
852  Part *format(int64_t value, int hint)
853  {
854  if (hint) {
855  switch (hint) {
856  case 'c': return format_c((char)value);
857  case 'u': return format((uint64_t)value, 0);
858  case 'o': return format_o64((uint64_t)value, 0);
859  case 'x': return format_x64((uint64_t)value, 0);
860 #ifndef STRFORMAT_NO_FP
861  case 'f': return format((double)value, 0);
862 #endif
863  }
864  }
865  return format_int64(value, q.plus);
866  }
867  Part *format(uint64_t value, int hint)
868  {
869  if (hint) {
870  switch (hint) {
871  case 'c': return format_c((char)value);
872  case 'd': return format((int64_t)value, 0);
873  case 'o': return format_oct64(value);
874  case 'x': return format_hex64(value, false);
875 #ifndef STRFORMAT_NO_FP
876  case 'f': return format((double)value, 0);
877 #endif
878  }
879  }
880  return format_uint64(value);
881  }
882  Part *format(char const *value, int hint)
883  {
884  if (!value) {
885  return alloc_part("(null)");
886  }
887  if (hint) {
888  switch (hint) {
889  case 'c':
890  return format_c(num<char>(value, q.opt));
891  case 'd':
892  if (q.lflag == 0) {
893  return format(num<int32_t>(value, q.opt), 0);
894  } else {
895  return format(num<int64_t>(value, q.opt), 0);
896  }
897  case 'u': case 'o': case 'x':
898  if (q.lflag == 0) {
899  return format(num<uint32_t>(value, q.opt), hint);
900  } else {
901  return format(num<uint64_t>(value, q.opt), hint);
902  }
903 #ifndef STRFORMAT_NO_FP
904  case 'f':
905  return format(num<double>(value, q.opt), hint);
906 #endif
907  }
908  }
909  return alloc_part(value, value + strlen(value));
910  }
911  Part *format(std::string_view const &value, int hint)
912  {
913  if (hint == 's') {
914  return alloc_part(value);
915  }
916  return format(value.data(), hint);
917  }
918  Part *format(std::vector<char> const &value, int hint)
919  {
920  std::string_view sv(value.data(), value.size());
921  if (hint == 's') {
922  return alloc_part(sv);
923  }
924  return format(sv, hint);
925  }
926  Part *format_p(void *val)
927  {
928  return format_pointer(val);
929  }
931  {
932  q.upper = false;
933  q.zero_padding = false;
934  q.align_left = false;
935  q.plus = false;
936  q.width = -1;
937  q.precision = -1;
938  q.lflag = 0;
939  }
940  void format(std::function<Part *(int)> const &callback, int width, int precision)
941  {
942  if (advance(false)) {
943  if (*q.next == '%') {
944  q.next++;
945  }
946 
948 
949  while (1) {
950  int c = (unsigned char)*q.next;
951  if (c == '0') {
952  q.zero_padding = true;
953  } else if (c == '+') {
954  q.plus = true;
955  } else if (c == '-') {
956  q.align_left = true;
957  } else {
958  break;
959  }
960  q.next++;
961  }
962 
963  auto GetNumber = [&](int alternate_value){
964  int value = -1;
965  if (*q.next == '*') {
966  q.next++;
967  } else {
968  while (1) {
969  int c = (unsigned char)*q.next;
970  if (!isdigit(c)) break;
971  if (value < 0) {
972  value = 0;
973  } else {
974  value *= 10;
975  }
976  value += c - '0';
977  q.next++;
978  }
979  }
980  if (value < 0) {
981  value = alternate_value;
982  }
983  return value;
984  };
985 
986  q.width = GetNumber(width);
987 
988  if (*q.next == '.') {
989  q.next++;
990  }
991 
992  q.precision = GetNumber(precision);
993 
994  while (*q.next == 'l') {
995  q.lflag++;
996  q.next++;
997  }
998 
999  Part *p = nullptr;
1000 
1001  int c = (unsigned char)*q.next;
1002  if (isupper(c)) {
1003  q.upper = true;
1004  c = tolower(c);
1005  }
1006  if (isalpha(c)) {
1007  p = callback(c);
1008  q.next++;
1009  }
1010  if (p) {
1011  int padlen = q.width - p->size;
1012  if (padlen > 0 && !q.align_left) {
1013  if (q.zero_padding) {
1014  char c = p->data[0];
1015  add_chars(&q.list, '0', padlen);
1016  if (c == '+' || c == '-') {
1017  q.list.last->data[0] = c;
1018  p->data[0] = '0';
1019  }
1020  } else {
1021  add_chars(&q.list, ' ', padlen);
1022  }
1023  }
1024 
1025  add_part(&q.list, p);
1026 
1027  if (padlen > 0 && q.align_left) {
1028  add_chars(&q.list, ' ', padlen);
1029  }
1030  }
1031 
1032  q.head = q.next;
1033  }
1034  }
1035  int length()
1036  {
1037  advance(true);
1038  int len = 0;
1039  for (Part *p = q.list.head; p; p = p->next) {
1040  len += p->size;
1041  }
1042  return len;
1043  }
1044 #ifndef STRFORMAT_NO_LOCALE
1045  void use_locale(bool use)
1046  {
1047  if (use) {
1048  q.opt.lc = localeconv();
1049  } else {
1050  q.opt.lc = nullptr;
1051  }
1052  }
1053 #endif
1054  void set_flags(int flags)
1055  {
1056 #ifndef STRFORMAT_NO_LOCALE
1057  use_locale(flags & Locale);
1058 #endif
1059  }
1060 public:
1062  void operator = (string_formatter const &) = delete;
1063 
1065  {
1066  q = r.q;
1067  r._init();
1068  }
1070  {
1071  clear();
1072  q = r.q;
1073  r._init();
1074  }
1075 
1076  string_formatter(int flags = 0, std::string const &text = {})
1077  {
1078  reset(flags, text);
1079  }
1080 
1081  string_formatter(std::string const &text)
1082  {
1083  reset(0, text);
1084  }
1086  {
1087  clear();
1088  }
1089 
1090  char decimal_point() const
1091  {
1092 #ifndef STRFORMAT_NO_LOCALE
1093  if (q.opt.lc && q.opt.lc->decimal_point) {
1094  return *q.opt.lc->decimal_point;
1095  }
1096 #endif
1097  return '.';
1098  }
1099 
1100  string_formatter &reset(int flags, std::string const &text)
1101  {
1102  clear();
1103  q.text = text;
1104  q.head = q.text.data();
1105  q.next = q.head;
1106 
1107 #ifndef STRFORMAT_NO_LOCALE
1108  use_locale(flags & Locale);
1109 #endif
1110 
1111  return *this;
1112  }
1113 
1114  string_formatter &append(std::string const &s)
1115  {
1116  q.text += s;
1117  return *this;
1118  }
1119  string_formatter &append(char const *s)
1120  {
1121  q.text += s;
1122  return *this;
1123  }
1124 
1125  template <typename T> string_formatter &arg(T const &value, int width = -1, int precision = -1)
1126  {
1127  format([&](int hint){ return format(value, hint); }, width, precision);
1128  return *this;
1129  }
1130 #ifndef STRFORMAT_NO_FP
1131  string_formatter &f(double value, int width = -1, int precision = -1)
1132  {
1133  return arg(value, width, precision);
1134  }
1135 #endif
1136  string_formatter &c(char value, int width = -1, int precision = -1)
1137  {
1138  return arg(value, width, precision);
1139  }
1140  string_formatter &d(int32_t value, int width = -1, int precision = -1)
1141  {
1142  return arg(value, width, precision);
1143  }
1144  string_formatter &ld(int64_t value, int width = -1, int precision = -1)
1145  {
1146  return arg(value, width, precision);
1147  }
1148  string_formatter &u(uint32_t value, int width = -1, int precision = -1)
1149  {
1150  return arg(value, width, precision);
1151  }
1152  string_formatter &lu(uint64_t value, int width = -1, int precision = -1)
1153  {
1154  return arg(value, width, precision);
1155  }
1156  string_formatter &o(int32_t value, int width = -1, int precision = -1)
1157  {
1158  format([&](int hint){ return format_o32(value, hint); }, width, precision);
1159  return *this;
1160  }
1161  string_formatter &lo(int64_t value, int width = -1, int precision = -1)
1162  {
1163  format([&](int hint){ return format_o64(value, hint); }, width, precision);
1164  return *this;
1165  }
1166  string_formatter &x(int32_t value, int width = -1, int precision = -1)
1167  {
1168  format([&](int hint){ return format_x32(value, hint); }, width, precision);
1169  return *this;
1170  }
1171  string_formatter &lx(int64_t value, int width = -1, int precision = -1)
1172  {
1173  format([&](int hint){ return format_x64(value, hint); }, width, precision);
1174  return *this;
1175  }
1176  string_formatter &s(char const *value, int width = -1, int precision = -1)
1177  {
1178  return arg(value, width, precision);
1179  }
1180  string_formatter &s(std::string_view const &value, int width = -1, int precision = -1)
1181  {
1182  return arg(value, width, precision);
1183  }
1184  string_formatter &p(void *value, int width = -1, int precision = -1)
1185  {
1186  format([&](int hint){ (void)hint; return format_p(value); }, width, precision);
1187  return *this;
1188  }
1189 
1190  template <typename T> string_formatter &operator () (T const &value, int width = -1, int precision = -1)
1191  {
1192  return arg(value, width, precision);
1193  }
1194  void render(std::function<void (char const *ptr, int len)> const &to)
1195  {
1196  advance(true);
1197  for (Part *p = q.list.head; p; p = p->next) {
1198  to(p->data, p->size);
1199  }
1200  }
1201  void vec(std::vector<char> *vec)
1202  {
1203  vec->reserve(vec->size() + length());
1204  render([&](char const *ptr, int len){
1205  vec->insert(vec->end(), ptr, ptr + len);
1206  });
1207  }
1208  void write_to(FILE *fp)
1209  {
1210  render([&](char const *ptr, int len){
1211  fwrite(ptr, 1, len, fp);
1212  });
1213  }
1214  void write_to(int fd)
1215  {
1216  render([&](char const *ptr, int len){
1217  ::write(fd, ptr, len);
1218  });
1219  }
1220  void put()
1221  {
1222  write_to(stdout);
1223  }
1224  void err()
1225  {
1226  write_to(stderr);
1227  }
1228  std::string str()
1229  {
1230  int n = length();
1231  char *p;
1232  std::vector<char> tmp;
1233  if (n < 4096) { // if smaller, use stack
1234  p = (char *)alloca(n);
1235  } else {
1236  tmp.reserve(n); // if larger, use heap
1237  p = tmp.data();
1238  }
1239  char *d = p;
1240  render([&](char const *ptr, int len){
1241  memcpy(d, ptr, len);
1242  d += len;
1243  });
1244  return std::string(p, n);
1245  }
1246  operator std::string ()
1247  {
1248  return str();
1249  }
1250 };
1251 
1252 } // namespace strformat_ns
1253 
1254 // using strformat = strformat_ns::string_formatter;
1256 
1257 #endif // STRFORMAT_H
Definition: strformat.h:34
static double my_strtod(const char *nptr, char **endptr)
Locale‑independent strtod clone.
Definition: strformat.h:77
static double pow10_int(int exp)
Return 10 raised to an integer power.
Definition: strformat.h:46
Definition: strformat.h:270
string_formatter & u(uint32_t value, int width=-1, int precision=-1)
Definition: strformat.h:1148
static void free_part(Part **p)
Definition: strformat.h:306
string_formatter & append(char const *s)
Definition: strformat.h:1119
string_formatter(int flags=0, std::string const &text={})
Definition: strformat.h:1076
void format(std::function< Part *(int)> const &callback, int width, int precision)
Definition: strformat.h:940
void write_to(int fd)
Definition: strformat.h:1214
Part * format(char c, int hint)
Definition: strformat.h:802
void use_locale(bool use)
Definition: strformat.h:1045
string_formatter & f(double value, int width=-1, int precision=-1)
Definition: strformat.h:1131
string_formatter(std::string const &text)
Definition: strformat.h:1081
Part * format(uint64_t value, int hint)
Definition: strformat.h:867
string_formatter & append(std::string const &s)
Definition: strformat.h:1114
void reset_format_params()
Definition: strformat.h:930
static Part * format_uint64(uint64_t val)
Definition: strformat.h:555
static Part * format_hex64(uint64_t val, bool upper)
Definition: strformat.h:637
static void free_list(PartList *list)
Definition: strformat.h:325
Part * format(int64_t value, int hint)
Definition: strformat.h:852
static Part * format_pointer(void *val)
Definition: strformat.h:658
string_formatter & arg(T const &value, int width=-1, int precision=-1)
Definition: strformat.h:1125
Part * format(std::vector< char > const &value, int hint)
Definition: strformat.h:918
Part * format_o32(uint32_t value, int hint)
Definition: strformat.h:742
string_formatter & lx(int64_t value, int width=-1, int precision=-1)
Definition: strformat.h:1171
static Part * format_int32(int32_t val, bool force_sign)
Definition: strformat.h:474
std::string str()
Definition: strformat.h:1228
string_formatter & d(int32_t value, int width=-1, int precision=-1)
Definition: strformat.h:1140
static void add_part(PartList *list, Part *part)
Definition: strformat.h:313
Part * format(std::string_view const &value, int hint)
Definition: strformat.h:911
Part * format_c(char c)
Definition: strformat.h:738
string_formatter(string_formatter &&r)
Definition: strformat.h:1064
string_formatter & s(std::string_view const &value, int width=-1, int precision=-1)
Definition: strformat.h:1180
static Part * format_uint32(uint32_t val)
Definition: strformat.h:505
Part * format_o64(uint64_t value, int hint)
Definition: strformat.h:757
string_formatter & ld(int64_t value, int width=-1, int precision=-1)
Definition: strformat.h:1144
char decimal_point() const
Definition: strformat.h:1090
static Part * format_oct32(uint32_t val)
Definition: strformat.h:574
static void add_chars(PartList *list, char c, int n)
Definition: strformat.h:336
string_formatter & lu(uint64_t value, int width=-1, int precision=-1)
Definition: strformat.h:1152
string_formatter(string_formatter const &)=delete
void clear()
Definition: strformat.h:697
string_formatter & operator()(T const &value, int width=-1, int precision=-1)
Definition: strformat.h:1190
Part * format_double(double val, int precision, bool trim_zeros, bool plus)
Definition: strformat.h:356
Part * format_f(double value, bool trim_zeros)
Definition: strformat.h:732
Part * format(char const *value, int hint)
Definition: strformat.h:882
void write_to(FILE *fp)
Definition: strformat.h:1208
Flags
Definition: strformat.h:272
@ Locale
Definition: strformat.h:273
static Part * alloc_part(const char *begin, const char *end)
Definition: strformat.h:294
Part * format(int32_t value, int hint)
Definition: strformat.h:822
Part * format(uint32_t value, int hint)
Definition: strformat.h:837
Part * format_p(void *val)
Definition: strformat.h:926
string_formatter & p(void *value, int width=-1, int precision=-1)
Definition: strformat.h:1184
string_formatter & lo(int64_t value, int width=-1, int precision=-1)
Definition: strformat.h:1161
~string_formatter()
Definition: strformat.h:1085
static Part * format_oct64(uint64_t val)
Definition: strformat.h:595
int length()
Definition: strformat.h:1035
static char const * digits_lower()
Definition: strformat.h:346
static Part * alloc_part(const char *data, int size)
Definition: strformat.h:285
Part * format_x32(uint32_t value, int hint)
Definition: strformat.h:772
void vec(std::vector< char > *vec)
Definition: strformat.h:1201
Part * format_x64(uint64_t value, int hint)
Definition: strformat.h:787
string_formatter & o(int32_t value, int width=-1, int precision=-1)
Definition: strformat.h:1156
static Part * alloc_part(const std::string_view &str)
Definition: strformat.h:302
struct strformat_ns::string_formatter::Private q
string_formatter & s(char const *value, int width=-1, int precision=-1)
Definition: strformat.h:1176
void _init()
Definition: strformat.h:692
static Part * format_hex32(uint32_t val, bool upper)
Definition: strformat.h:616
string_formatter & reset(int flags, std::string const &text)
Definition: strformat.h:1100
void err()
Definition: strformat.h:1224
bool advance(bool complete)
Definition: strformat.h:701
void render(std::function< void(char const *ptr, int len)> const &to)
Definition: strformat.h:1194
void put()
Definition: strformat.h:1220
string_formatter & c(char value, int width=-1, int precision=-1)
Definition: strformat.h:1136
string_formatter & x(int32_t value, int width=-1, int precision=-1)
Definition: strformat.h:1166
static Part * alloc_part(const char *str)
Definition: strformat.h:298
static Part * format_int64(int64_t val, bool force_sign)
Definition: strformat.h:524
static char const * digits_upper()
Definition: strformat.h:350
Part * format(double value, int hint)
Definition: strformat.h:807
void set_flags(int flags)
Definition: strformat.h:1054
void operator=(string_formatter const &)=delete
Definition: strformat.h:32
uint32_t num< uint32_t >(char const *value, Option_ const &opt)
Definition: strformat.h:229
int64_t num< int64_t >(char const *value, Option_ const &opt)
Definition: strformat.h:235
double num< double >(char const *value, Option_ const &opt)
Definition: strformat.h:248
int32_t num< int32_t >(char const *value, Option_ const &opt)
Definition: strformat.h:223
static T parse_number(char const *ptr, std::function< T(char const *p, int radix)> conv)
Definition: strformat.h:200
static T num(char const *value, Option_ const &opt)
char num< char >(char const *value, Option_ const &opt)
Definition: strformat.h:217
uint64_t num< uint64_t >(char const *value, Option_ const &opt)
Definition: strformat.h:241
Definition: strformat.h:167
int radix
Definition: strformat.h:170
char const * p
Definition: strformat.h:168
NumberParser(char const *ptr)
Definition: strformat.h:171
bool sign
Definition: strformat.h:169
Definition: strformat.h:208
struct lconv * lc
Definition: strformat.h:212
Definition: strformat.h:281
Part * head
Definition: strformat.h:282
Part * last
Definition: strformat.h:283
Definition: strformat.h:276
int size
Definition: strformat.h:278
Part * next
Definition: strformat.h:277
char data[1]
Definition: strformat.h:279
Definition: strformat.h:677
std::string text
Definition: strformat.h:678
bool plus
Definition: strformat.h:685
char const * next
Definition: strformat.h:680
int width
Definition: strformat.h:686
PartList list
Definition: strformat.h:681
bool zero_padding
Definition: strformat.h:683
Option_ opt
Definition: strformat.h:689
bool upper
Definition: strformat.h:682
bool align_left
Definition: strformat.h:684
int lflag
Definition: strformat.h:688
int precision
Definition: strformat.h:687
char const * head
Definition: strformat.h:679