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