Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/rapidyaml/include/c4/format.hpp
4261 views
1
#ifndef _C4_FORMAT_HPP_
2
#define _C4_FORMAT_HPP_
3
4
/** @file format.hpp provides type-safe facilities for formatting arguments
5
* to string buffers */
6
7
#include "c4/charconv.hpp"
8
#include "c4/blob.hpp"
9
10
11
#ifdef _MSC_VER
12
# pragma warning(push)
13
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
14
# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning)
15
# endif
16
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
17
#elif defined(__clang__)
18
# pragma clang diagnostic push
19
#elif defined(__GNUC__)
20
# pragma GCC diagnostic push
21
# pragma GCC diagnostic ignored "-Wuseless-cast"
22
#endif
23
24
namespace c4 {
25
26
27
//-----------------------------------------------------------------------------
28
//-----------------------------------------------------------------------------
29
//-----------------------------------------------------------------------------
30
// formatting truthy types as booleans
31
32
namespace fmt {
33
34
/** write a variable as an alphabetic boolean, ie as either true or false
35
* @param strict_read */
36
template<class T>
37
struct boolalpha_
38
{
39
boolalpha_(T val_, bool strict_read_=false) : val(val_ ? true : false), strict_read(strict_read_) {}
40
bool val;
41
bool strict_read;
42
};
43
44
template<class T>
45
boolalpha_<T> boolalpha(T const& val, bool strict_read=false)
46
{
47
return boolalpha_<T>(val, strict_read);
48
}
49
50
} // namespace fmt
51
52
/** write a variable as an alphabetic boolean, ie as either true or false */
53
template<class T>
54
inline size_t to_chars(substr buf, fmt::boolalpha_<T> fmt)
55
{
56
return to_chars(buf, fmt.val ? "true" : "false");
57
}
58
59
60
61
//-----------------------------------------------------------------------------
62
//-----------------------------------------------------------------------------
63
//-----------------------------------------------------------------------------
64
// formatting integral types
65
66
namespace fmt {
67
68
/** format an integral type with a custom radix */
69
template<typename T>
70
struct integral_
71
{
72
T val;
73
T radix;
74
C4_ALWAYS_INLINE integral_(T val_, T radix_) : val(val_), radix(radix_) {}
75
};
76
77
/** format an integral type with a custom radix, and pad with zeroes on the left */
78
template<typename T>
79
struct integral_padded_
80
{
81
T val;
82
T radix;
83
size_t num_digits;
84
C4_ALWAYS_INLINE integral_padded_(T val_, T radix_, size_t nd) : val(val_), radix(radix_), num_digits(nd) {}
85
};
86
87
/** format an integral type with a custom radix */
88
template<class T>
89
C4_ALWAYS_INLINE integral_<T> integral(T val, T radix=10)
90
{
91
return integral_<T>(val, radix);
92
}
93
/** format an integral type with a custom radix */
94
template<class T>
95
C4_ALWAYS_INLINE integral_<intptr_t> integral(T const* val, T radix=10)
96
{
97
return integral_<intptr_t>(reinterpret_cast<intptr_t>(val), static_cast<intptr_t>(radix));
98
}
99
/** format an integral type with a custom radix */
100
template<class T>
101
C4_ALWAYS_INLINE integral_<intptr_t> integral(std::nullptr_t, T radix=10)
102
{
103
return integral_<intptr_t>(intptr_t(0), static_cast<intptr_t>(radix));
104
}
105
/** pad the argument with zeroes on the left, with decimal radix */
106
template<class T>
107
C4_ALWAYS_INLINE integral_padded_<T> zpad(T val, size_t num_digits)
108
{
109
return integral_padded_<T>(val, T(10), num_digits);
110
}
111
/** pad the argument with zeroes on the left */
112
template<class T>
113
C4_ALWAYS_INLINE integral_padded_<T> zpad(integral_<T> val, size_t num_digits)
114
{
115
return integral_padded_<T>(val.val, val.radix, num_digits);
116
}
117
/** pad the argument with zeroes on the left */
118
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(std::nullptr_t, size_t num_digits)
119
{
120
return integral_padded_<intptr_t>(0, 16, num_digits);
121
}
122
/** pad the argument with zeroes on the left */
123
template<class T>
124
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T const* val, size_t num_digits)
125
{
126
return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);
127
}
128
template<class T>
129
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T * val, size_t num_digits)
130
{
131
return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);
132
}
133
134
135
/** format the pointer as an hexadecimal value */
136
template<class T>
137
inline integral_<intptr_t> hex(T * v)
138
{
139
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));
140
}
141
/** format the pointer as an hexadecimal value */
142
template<class T>
143
inline integral_<intptr_t> hex(T const* v)
144
{
145
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));
146
}
147
/** format null as an hexadecimal value
148
* @overload hex */
149
inline integral_<intptr_t> hex(std::nullptr_t)
150
{
151
return integral_<intptr_t>(0, intptr_t(16));
152
}
153
/** format the integral_ argument as an hexadecimal value
154
* @overload hex */
155
template<class T>
156
inline integral_<T> hex(T v)
157
{
158
return integral_<T>(v, T(16));
159
}
160
161
/** format the pointer as an octal value */
162
template<class T>
163
inline integral_<intptr_t> oct(T const* v)
164
{
165
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));
166
}
167
/** format the pointer as an octal value */
168
template<class T>
169
inline integral_<intptr_t> oct(T * v)
170
{
171
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));
172
}
173
/** format null as an octal value */
174
inline integral_<intptr_t> oct(std::nullptr_t)
175
{
176
return integral_<intptr_t>(intptr_t(0), intptr_t(8));
177
}
178
/** format the integral_ argument as an octal value */
179
template<class T>
180
inline integral_<T> oct(T v)
181
{
182
return integral_<T>(v, T(8));
183
}
184
185
/** format the pointer as a binary 0-1 value
186
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
187
template<class T>
188
inline integral_<intptr_t> bin(T const* v)
189
{
190
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));
191
}
192
/** format the pointer as a binary 0-1 value
193
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
194
template<class T>
195
inline integral_<intptr_t> bin(T * v)
196
{
197
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));
198
}
199
/** format null as a binary 0-1 value
200
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
201
inline integral_<intptr_t> bin(std::nullptr_t)
202
{
203
return integral_<intptr_t>(intptr_t(0), intptr_t(2));
204
}
205
/** format the integral_ argument as a binary 0-1 value
206
* @see c4::raw() if you want to use a raw memcpy-based binary dump instead of 0-1 formatting */
207
template<class T>
208
inline integral_<T> bin(T v)
209
{
210
return integral_<T>(v, T(2));
211
}
212
213
214
template<class T>
215
struct overflow_checked_
216
{
217
static_assert(std::is_integral<T>::value, "range checking only for integral types");
218
C4_ALWAYS_INLINE overflow_checked_(T &val_) : val(&val_) {}
219
T *val;
220
};
221
template<class T>
222
C4_ALWAYS_INLINE overflow_checked_<T> overflow_checked(T &val)
223
{
224
return overflow_checked_<T>(val);
225
}
226
227
} // namespace fmt
228
229
/** format an integral_ signed type */
230
template<typename T>
231
C4_ALWAYS_INLINE
232
typename std::enable_if<std::is_signed<T>::value, size_t>::type
233
to_chars(substr buf, fmt::integral_<T> fmt)
234
{
235
return itoa(buf, fmt.val, fmt.radix);
236
}
237
/** format an integral_ signed type, pad with zeroes */
238
template<typename T>
239
C4_ALWAYS_INLINE
240
typename std::enable_if<std::is_signed<T>::value, size_t>::type
241
to_chars(substr buf, fmt::integral_padded_<T> fmt)
242
{
243
return itoa(buf, fmt.val, fmt.radix, fmt.num_digits);
244
}
245
246
/** format an integral_ unsigned type */
247
template<typename T>
248
C4_ALWAYS_INLINE
249
typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
250
to_chars(substr buf, fmt::integral_<T> fmt)
251
{
252
return utoa(buf, fmt.val, fmt.radix);
253
}
254
/** format an integral_ unsigned type, pad with zeroes */
255
template<typename T>
256
C4_ALWAYS_INLINE
257
typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
258
to_chars(substr buf, fmt::integral_padded_<T> fmt)
259
{
260
return utoa(buf, fmt.val, fmt.radix, fmt.num_digits);
261
}
262
263
template<class T>
264
C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> wrapper)
265
{
266
if(C4_LIKELY(!overflows<T>(s)))
267
return atox(s, wrapper.val);
268
return false;
269
}
270
271
272
//-----------------------------------------------------------------------------
273
//-----------------------------------------------------------------------------
274
//-----------------------------------------------------------------------------
275
// formatting real types
276
277
namespace fmt {
278
279
template<class T>
280
struct real_
281
{
282
T val;
283
int precision;
284
RealFormat_e fmt;
285
real_(T v, int prec=-1, RealFormat_e f=FTOA_FLOAT) : val(v), precision(prec), fmt(f) {}
286
};
287
288
template<class T>
289
real_<T> real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
290
{
291
return real_<T>(val, precision, fmt);
292
}
293
294
} // namespace fmt
295
296
inline size_t to_chars(substr buf, fmt::real_< float> fmt) { return ftoa(buf, fmt.val, fmt.precision, fmt.fmt); }
297
inline size_t to_chars(substr buf, fmt::real_<double> fmt) { return dtoa(buf, fmt.val, fmt.precision, fmt.fmt); }
298
299
300
//-----------------------------------------------------------------------------
301
//-----------------------------------------------------------------------------
302
//-----------------------------------------------------------------------------
303
// writing raw binary data
304
305
namespace fmt {
306
307
/** @see blob_ */
308
template<class T>
309
struct raw_wrapper_ : public blob_<T>
310
{
311
size_t alignment;
312
313
C4_ALWAYS_INLINE raw_wrapper_(blob_<T> data, size_t alignment_) noexcept
314
:
315
blob_<T>(data),
316
alignment(alignment_)
317
{
318
C4_ASSERT_MSG(alignment > 0 && (alignment & (alignment - 1)) == 0, "alignment must be a power of two");
319
}
320
};
321
322
using const_raw_wrapper = raw_wrapper_<cbyte>;
323
using raw_wrapper = raw_wrapper_<byte>;
324
325
/** mark a variable to be written in raw binary format, using memcpy
326
* @see blob_ */
327
inline const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t))
328
{
329
return const_raw_wrapper(data, alignment);
330
}
331
/** mark a variable to be written in raw binary format, using memcpy
332
* @see blob_ */
333
inline const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t))
334
{
335
return const_raw_wrapper(data, alignment);
336
}
337
/** mark a variable to be written in raw binary format, using memcpy
338
* @see blob_ */
339
template<class T>
340
inline const_raw_wrapper craw(T const& C4_RESTRICT data, size_t alignment=alignof(T))
341
{
342
return const_raw_wrapper(cblob(data), alignment);
343
}
344
/** mark a variable to be written in raw binary format, using memcpy
345
* @see blob_ */
346
template<class T>
347
inline const_raw_wrapper raw(T const& C4_RESTRICT data, size_t alignment=alignof(T))
348
{
349
return const_raw_wrapper(cblob(data), alignment);
350
}
351
352
/** mark a variable to be read in raw binary format, using memcpy */
353
inline raw_wrapper raw(blob data, size_t alignment=alignof(max_align_t))
354
{
355
return raw_wrapper(data, alignment);
356
}
357
/** mark a variable to be read in raw binary format, using memcpy */
358
template<class T>
359
inline raw_wrapper raw(T & C4_RESTRICT data, size_t alignment=alignof(T))
360
{
361
return raw_wrapper(blob(data), alignment);
362
}
363
364
} // namespace fmt
365
366
367
/** write a variable in raw binary format, using memcpy */
368
C4CORE_EXPORT size_t to_chars(substr buf, fmt::const_raw_wrapper r);
369
370
/** read a variable in raw binary format, using memcpy */
371
C4CORE_EXPORT bool from_chars(csubstr buf, fmt::raw_wrapper *r);
372
/** read a variable in raw binary format, using memcpy */
373
inline bool from_chars(csubstr buf, fmt::raw_wrapper r)
374
{
375
return from_chars(buf, &r);
376
}
377
378
/** read a variable in raw binary format, using memcpy */
379
inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper *r)
380
{
381
return from_chars(buf, r);
382
}
383
/** read a variable in raw binary format, using memcpy */
384
inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper r)
385
{
386
return from_chars(buf, &r);
387
}
388
389
390
//-----------------------------------------------------------------------------
391
//-----------------------------------------------------------------------------
392
//-----------------------------------------------------------------------------
393
// formatting aligned to left/right
394
395
namespace fmt {
396
397
template<class T>
398
struct left_
399
{
400
T val;
401
size_t width;
402
char pad;
403
left_(T v, size_t w, char p) : val(v), width(w), pad(p) {}
404
};
405
406
template<class T>
407
struct right_
408
{
409
T val;
410
size_t width;
411
char pad;
412
right_(T v, size_t w, char p) : val(v), width(w), pad(p) {}
413
};
414
415
/** mark an argument to be aligned left */
416
template<class T>
417
left_<T> left(T val, size_t width, char padchar=' ')
418
{
419
return left_<T>(val, width, padchar);
420
}
421
422
/** mark an argument to be aligned right */
423
template<class T>
424
right_<T> right(T val, size_t width, char padchar=' ')
425
{
426
return right_<T>(val, width, padchar);
427
}
428
429
} // namespace fmt
430
431
432
template<class T>
433
size_t to_chars(substr buf, fmt::left_<T> const& C4_RESTRICT align)
434
{
435
size_t ret = to_chars(buf, align.val);
436
if(ret >= buf.len || ret >= align.width)
437
return ret > align.width ? ret : align.width;
438
buf.first(align.width).sub(ret).fill(align.pad);
439
to_chars(buf, align.val);
440
return align.width;
441
}
442
443
template<class T>
444
size_t to_chars(substr buf, fmt::right_<T> const& C4_RESTRICT align)
445
{
446
size_t ret = to_chars(buf, align.val);
447
if(ret >= buf.len || ret >= align.width)
448
return ret > align.width ? ret : align.width;
449
size_t rem = static_cast<size_t>(align.width - ret);
450
buf.first(rem).fill(align.pad);
451
to_chars(buf.sub(rem), align.val);
452
return align.width;
453
}
454
455
456
//-----------------------------------------------------------------------------
457
//-----------------------------------------------------------------------------
458
//-----------------------------------------------------------------------------
459
460
/// @cond dev
461
// terminates the variadic recursion
462
inline size_t cat(substr /*buf*/)
463
{
464
return 0;
465
}
466
/// @endcond
467
468
469
/** serialize the arguments, concatenating them to the given fixed-size buffer.
470
* The buffer size is strictly respected: no writes will occur beyond its end.
471
* @return the number of characters needed to write all the arguments into the buffer.
472
* @see c4::catrs() if instead of a fixed-size buffer, a resizeable container is desired
473
* @see c4::uncat() for the inverse function
474
* @see c4::catsep() if a separator between each argument is to be used
475
* @see c4::format() if a format string is desired */
476
template<class Arg, class... Args>
477
size_t cat(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
478
{
479
size_t num = to_chars(buf, a);
480
buf = buf.len >= num ? buf.sub(num) : substr{};
481
num += cat(buf, more...);
482
return num;
483
}
484
485
/** like c4::cat() but return a substr instead of a size */
486
template<class... Args>
487
substr cat_sub(substr buf, Args && ...args)
488
{
489
size_t sz = cat(buf, std::forward<Args>(args)...);
490
C4_CHECK(sz <= buf.len);
491
return {buf.str, sz <= buf.len ? sz : buf.len};
492
}
493
494
495
//-----------------------------------------------------------------------------
496
497
/// @cond dev
498
// terminates the variadic recursion
499
inline size_t uncat(csubstr /*buf*/)
500
{
501
return 0;
502
}
503
/// @endcond
504
505
506
/** deserialize the arguments from the given buffer.
507
*
508
* @return the number of characters read from the buffer, or csubstr::npos
509
* if a conversion was not successful.
510
* @see c4::cat(). c4::uncat() is the inverse of c4::cat(). */
511
template<class Arg, class... Args>
512
size_t uncat(csubstr buf, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
513
{
514
size_t out = from_chars_first(buf, &a);
515
if(C4_UNLIKELY(out == csubstr::npos))
516
return csubstr::npos;
517
buf = buf.len >= out ? buf.sub(out) : substr{};
518
size_t num = uncat(buf, more...);
519
if(C4_UNLIKELY(num == csubstr::npos))
520
return csubstr::npos;
521
return out + num;
522
}
523
524
525
526
//-----------------------------------------------------------------------------
527
//-----------------------------------------------------------------------------
528
//-----------------------------------------------------------------------------
529
530
namespace detail {
531
532
template<class Sep>
533
C4_ALWAYS_INLINE size_t catsep_more(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)
534
{
535
return 0;
536
}
537
538
template<class Sep, class Arg, class... Args>
539
size_t catsep_more(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
540
{
541
size_t ret = to_chars(buf, sep);
542
size_t num = ret;
543
buf = buf.len >= ret ? buf.sub(ret) : substr{};
544
ret = to_chars(buf, a);
545
num += ret;
546
buf = buf.len >= ret ? buf.sub(ret) : substr{};
547
ret = catsep_more(buf, sep, more...);
548
num += ret;
549
return num;
550
}
551
552
553
template<class Sep>
554
inline size_t uncatsep_more(csubstr /*buf*/, Sep & /*sep*/)
555
{
556
return 0;
557
}
558
559
template<class Sep, class Arg, class... Args>
560
size_t uncatsep_more(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
561
{
562
size_t ret = from_chars_first(buf, &sep);
563
size_t num = ret;
564
if(C4_UNLIKELY(ret == csubstr::npos))
565
return csubstr::npos;
566
buf = buf.len >= ret ? buf.sub(ret) : substr{};
567
ret = from_chars_first(buf, &a);
568
if(C4_UNLIKELY(ret == csubstr::npos))
569
return csubstr::npos;
570
num += ret;
571
buf = buf.len >= ret ? buf.sub(ret) : substr{};
572
ret = uncatsep_more(buf, sep, more...);
573
if(C4_UNLIKELY(ret == csubstr::npos))
574
return csubstr::npos;
575
num += ret;
576
return num;
577
}
578
579
} // namespace detail
580
581
/// @cond dev
582
template<class Sep>
583
size_t catsep(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)
584
{
585
return 0;
586
}
587
/// @endcond
588
589
/** serialize the arguments, concatenating them to the given fixed-size
590
* buffer, using a separator between each argument.
591
* The buffer size is strictly respected: no writes will occur beyond its end.
592
* @return the number of characters needed to write all the arguments into the buffer.
593
* @see c4::catseprs() if instead of a fixed-size buffer, a resizeable container is desired
594
* @see c4::uncatsep() for the inverse function (ie, reading instead of writing)
595
* @see c4::cat() if no separator is needed
596
* @see c4::format() if a format string is desired */
597
template<class Sep, class Arg, class... Args>
598
size_t catsep(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
599
{
600
size_t num = to_chars(buf, a);
601
buf = buf.len >= num ? buf.sub(num) : substr{};
602
num += detail::catsep_more(buf, sep, more...);
603
return num;
604
}
605
606
/** like c4::catsep() but return a substr instead of a size
607
* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */
608
template<class... Args>
609
substr catsep_sub(substr buf, Args && ...args)
610
{
611
size_t sz = catsep(buf, std::forward<Args>(args)...);
612
C4_CHECK(sz <= buf.len);
613
return {buf.str, sz <= buf.len ? sz : buf.len};
614
}
615
616
/** deserialize the arguments from the given buffer, using a separator.
617
*
618
* @return the number of characters read from the buffer, or csubstr::npos
619
* if a conversion was not successful
620
* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */
621
template<class Sep, class Arg, class... Args>
622
size_t uncatsep(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
623
{
624
size_t ret = from_chars_first(buf, &a), num = ret;
625
if(C4_UNLIKELY(ret == csubstr::npos))
626
return csubstr::npos;
627
buf = buf.len >= ret ? buf.sub(ret) : substr{};
628
ret = detail::uncatsep_more(buf, sep, more...);
629
if(C4_UNLIKELY(ret == csubstr::npos))
630
return csubstr::npos;
631
num += ret;
632
return num;
633
}
634
635
636
//-----------------------------------------------------------------------------
637
//-----------------------------------------------------------------------------
638
//-----------------------------------------------------------------------------
639
640
/// @cond dev
641
// terminates the variadic recursion
642
inline size_t format(substr buf, csubstr fmt)
643
{
644
return to_chars(buf, fmt);
645
}
646
/// @endcond
647
648
649
/** using a format string, serialize the arguments into the given
650
* fixed-size buffer.
651
* The buffer size is strictly respected: no writes will occur beyond its end.
652
* In the format string, each argument is marked with a compact
653
* curly-bracket pair: {}. Arguments beyond the last curly bracket pair
654
* are silently ignored. For example:
655
* @code{.cpp}
656
* c4::format(buf, "the {} drank {} {}", "partier", 5, "beers"); // the partier drank 5 beers
657
* c4::format(buf, "the {} drank {} {}", "programmer", 6, "coffees"); // the programmer drank 6 coffees
658
* @endcode
659
* @return the number of characters needed to write into the buffer.
660
* @see c4::formatrs() if instead of a fixed-size buffer, a resizeable container is desired
661
* @see c4::unformat() for the inverse function
662
* @see c4::cat() if no format or separator is needed
663
* @see c4::catsep() if no format is needed, but a separator must be used */
664
template<class Arg, class... Args>
665
size_t format(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
666
{
667
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
668
if(C4_UNLIKELY(pos == csubstr::npos))
669
return to_chars(buf, fmt);
670
size_t num = to_chars(buf, fmt.sub(0, pos));
671
size_t out = num;
672
buf = buf.len >= num ? buf.sub(num) : substr{};
673
num = to_chars(buf, a);
674
out += num;
675
buf = buf.len >= num ? buf.sub(num) : substr{};
676
num = format(buf, fmt.sub(pos + 2), more...);
677
out += num;
678
return out;
679
}
680
681
/** like c4::format() but return a substr instead of a size
682
* @see c4::format()
683
* @see c4::catsep(). uncatsep() is the inverse of catsep(). */
684
template<class... Args>
685
substr format_sub(substr buf, csubstr fmt, Args const& C4_RESTRICT ...args)
686
{
687
size_t sz = c4::format(buf, fmt, args...);
688
C4_CHECK(sz <= buf.len);
689
return {buf.str, sz <= buf.len ? sz : buf.len};
690
}
691
692
693
//-----------------------------------------------------------------------------
694
695
/// @cond dev
696
// terminates the variadic recursion
697
inline size_t unformat(csubstr /*buf*/, csubstr fmt)
698
{
699
return fmt.len;
700
}
701
/// @endcond
702
703
704
/** using a format string, deserialize the arguments from the given
705
* buffer.
706
* @return the number of characters read from the buffer, or npos if a conversion failed.
707
* @see c4::format(). c4::unformat() is the inverse function to format(). */
708
template<class Arg, class... Args>
709
size_t unformat(csubstr buf, csubstr fmt, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
710
{
711
const size_t pos = fmt.find("{}");
712
if(C4_UNLIKELY(pos == csubstr::npos))
713
return unformat(buf, fmt);
714
size_t num = pos;
715
size_t out = num;
716
buf = buf.len >= num ? buf.sub(num) : substr{};
717
num = from_chars_first(buf, &a);
718
if(C4_UNLIKELY(num == csubstr::npos))
719
return csubstr::npos;
720
out += num;
721
buf = buf.len >= num ? buf.sub(num) : substr{};
722
num = unformat(buf, fmt.sub(pos + 2), more...);
723
if(C4_UNLIKELY(num == csubstr::npos))
724
return csubstr::npos;
725
out += num;
726
return out;
727
}
728
729
730
//-----------------------------------------------------------------------------
731
//-----------------------------------------------------------------------------
732
//-----------------------------------------------------------------------------
733
734
/** like c4::cat(), but receives a container, and resizes it as needed to contain
735
* the result. The container is overwritten. To append to it, use the append
736
* overload.
737
* @see c4::cat() */
738
template<class CharOwningContainer, class... Args>
739
inline void catrs(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)
740
{
741
retry:
742
substr buf = to_substr(*cont);
743
size_t ret = cat(buf, args...);
744
cont->resize(ret);
745
if(ret > buf.len)
746
goto retry;
747
}
748
749
/** like c4::cat(), but creates and returns a new container sized as needed to contain
750
* the result.
751
* @see c4::cat() */
752
template<class CharOwningContainer, class... Args>
753
inline CharOwningContainer catrs(Args const& C4_RESTRICT ...args)
754
{
755
CharOwningContainer cont;
756
catrs(&cont, args...);
757
return cont;
758
}
759
760
/** like c4::cat(), but receives a container, and appends to it instead of
761
* overwriting it. The container is resized as needed to contain the result.
762
* @return the region newly appended to the original container
763
* @see c4::cat()
764
* @see c4::catrs() */
765
template<class CharOwningContainer, class... Args>
766
inline csubstr catrs_append(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)
767
{
768
const size_t pos = cont->size();
769
retry:
770
substr buf = to_substr(*cont).sub(pos);
771
size_t ret = cat(buf, args...);
772
cont->resize(pos + ret);
773
if(ret > buf.len)
774
goto retry;
775
return to_csubstr(*cont).range(pos, cont->size());
776
}
777
778
779
//-----------------------------------------------------------------------------
780
781
/** like c4::catsep(), but receives a container, and resizes it as needed to contain the result.
782
* The container is overwritten. To append to the container use the append overload.
783
* @see c4::catsep() */
784
template<class CharOwningContainer, class Sep, class... Args>
785
inline void catseprs(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
786
{
787
retry:
788
substr buf = to_substr(*cont);
789
size_t ret = catsep(buf, sep, args...);
790
cont->resize(ret);
791
if(ret > buf.len)
792
goto retry;
793
}
794
795
/** like c4::catsep(), but create a new container with the result.
796
* @return the requested container */
797
template<class CharOwningContainer, class Sep, class... Args>
798
inline CharOwningContainer catseprs(Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
799
{
800
CharOwningContainer cont;
801
catseprs(&cont, sep, args...);
802
return cont;
803
}
804
805
806
/** like catsep(), but receives a container, and appends the arguments, resizing the
807
* container as needed to contain the result. The buffer is appended to.
808
* @return a csubstr of the appended part
809
* @ingroup formatting_functions */
810
template<class CharOwningContainer, class Sep, class... Args>
811
inline csubstr catseprs_append(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
812
{
813
const size_t pos = cont->size();
814
retry:
815
substr buf = to_substr(*cont).sub(pos);
816
size_t ret = catsep(buf, sep, args...);
817
cont->resize(pos + ret);
818
if(ret > buf.len)
819
goto retry;
820
return to_csubstr(*cont).range(pos, cont->size());
821
}
822
823
824
//-----------------------------------------------------------------------------
825
826
/** like c4::format(), but receives a container, and resizes it as needed
827
* to contain the result. The container is overwritten. To append to
828
* the container use the append overload.
829
* @see c4::format() */
830
template<class CharOwningContainer, class... Args>
831
inline void formatrs(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
832
{
833
retry:
834
substr buf = to_substr(*cont);
835
size_t ret = format(buf, fmt, args...);
836
cont->resize(ret);
837
if(ret > buf.len)
838
goto retry;
839
}
840
841
/** like c4::format(), but create a new container with the result.
842
* @return the requested container */
843
template<class CharOwningContainer, class... Args>
844
inline CharOwningContainer formatrs(csubstr fmt, Args const& C4_RESTRICT ...args)
845
{
846
CharOwningContainer cont;
847
formatrs(&cont, fmt, args...);
848
return cont;
849
}
850
851
/** like format(), but receives a container, and appends the
852
* arguments, resizing the container as needed to contain the
853
* result. The buffer is appended to.
854
* @return the region newly appended to the original container
855
* @ingroup formatting_functions */
856
template<class CharOwningContainer, class... Args>
857
inline csubstr formatrs_append(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
858
{
859
const size_t pos = cont->size();
860
retry:
861
substr buf = to_substr(*cont).sub(pos);
862
size_t ret = format(buf, fmt, args...);
863
cont->resize(pos + ret);
864
if(ret > buf.len)
865
goto retry;
866
return to_csubstr(*cont).range(pos, cont->size());
867
}
868
869
} // namespace c4
870
871
#ifdef _MSC_VER
872
# pragma warning(pop)
873
#elif defined(__clang__)
874
# pragma clang diagnostic pop
875
#elif defined(__GNUC__)
876
# pragma GCC diagnostic pop
877
#endif
878
879
#endif /* _C4_FORMAT_HPP_ */
880
881