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