LibSWOC++ 1.5.14
Solid Wall of C++
Loading...
Searching...
No Matches
bw_format.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright Apache Software Foundation 2019
3
8
9#include <array>
10#include <cctype>
11#include <chrono>
12#include <cmath>
13#include <ctime>
14#include <cmath>
15#include <sys/param.h>
16#include <unistd.h>
17
18#include "swoc/BufferWriter.h"
19#include "swoc/bwf_base.h"
20#include "swoc/bwf_ex.h"
21#include "swoc/swoc_meta.h"
22#include "swoc/DiscreteRange.h"
23
24using namespace std::literals;
25using namespace swoc::literals;
26
27
29
30namespace swoc { inline namespace SWOC_VERSION_NS {
31
32namespace bwf {
37
39
40#pragma GCC diagnostic ignored "-Wchar-subscripts"
41
43 memset(_data, 0, sizeof(_data));
47 _data['g'] = TYPE_CHAR;
49 _data['p'] = TYPE_CHAR;
51 _data['s'] = TYPE_CHAR;
55
59
60 _data['<'] = static_cast<uint8_t>(Spec::Align::LEFT);
61 _data['>'] = static_cast<uint8_t>(Spec::Align::RIGHT);
62 _data['^'] = static_cast<uint8_t>(Spec::Align::CENTER);
63 _data['='] = static_cast<uint8_t>(Spec::Align::SIGN);
64}
65
66Spec::Spec(const TextView &fmt) {
67 this->parse(fmt);
68}
69
71bool
73 TextView num; // temporary for number parsing.
74
75 _name = fmt.take_prefix_at(':');
76 // if it's parsable as a number, treat it as an index.
77 num = _name;
78 auto n = svto_radix<10>(num);
79 if (num.empty()) {
80 _idx = static_cast<decltype(_idx)>(n);
81 }
82
83 if (fmt.size()) {
84 TextView sz = fmt.take_prefix_at(':'); // the format specifier.
85 _ext = fmt; // anything past the second ':' is the extension.
86 if (sz.size()) {
87 // fill and alignment
88 if ('%' == *sz) { // enable URI encoding of the fill character so
89 // metasyntactic chars can be used if needed.
90 if (sz.size() < 4) {
91 throw std::invalid_argument("Fill URI encoding without 2 hex characters and align mark");
92 }
93 if (Align::NONE == (_align = align_of(sz[3]))) {
94 throw std::invalid_argument("Fill URI without alignment mark");
95 }
96 char d1 = sz[1], d0 = sz[2];
97 if (!isxdigit(d0) || !isxdigit(d1)) {
98 throw std::invalid_argument("URI encoding with non-hex characters");
99 }
100 _fill = isdigit(d0) ? d0 - '0' : tolower(d0) - 'a' + 10;
101 _fill += (isdigit(d1) ? d1 - '0' : tolower(d1) - 'a' + 10) << 4;
102 sz += 4;
103 } else if (sz.size() > 1 && Align::NONE != (_align = align_of(sz[1]))) {
104 _fill = *sz;
105 sz += 2;
106 } else if (Align::NONE != (_align = align_of(*sz))) {
107 ++sz;
108 }
109 if (!sz.size()) {
110 return true;
111 }
112 // sign
113 if (is_sign(*sz)) {
114 _sign = *sz;
115 if (!(++sz).size()) {
116 return true;
117 }
118 }
119 // radix prefix
120 if ('#' == *sz) {
121 _radix_lead_p = true;
122 if (!(++sz).size()) {
123 return true;
124 }
125 }
126 // 0 fill for integers
127 if ('0' == *sz) {
128 if (Align::NONE == _align) {
130 }
131 _fill = '0';
132 ++sz;
133 }
134 num = sz;
135 n = svto_radix<10>(num);
136 if (num.size() < sz.size()) {
137 _min = static_cast<decltype(_min)>(n);
138 sz = num;
139 if (!sz.size()) {
140 return true;
141 }
142 }
143 // precision
144 if ('.' == *sz) {
145 num = ++sz;
146 n = svto_radix<10>(num);
147 if (num.size() < sz.size()) {
148 _prec = static_cast<decltype(_prec)>(n);
149 sz = num;
150 if (!sz.size()) {
151 return true;
152 }
153 } else {
154 throw std::invalid_argument("Precision mark without precision");
155 }
156 }
157 // style (type). Hex, octal, etc.
158 if (is_type(*sz)) {
159 _type = *sz;
160 if (!(++sz).size()) {
161 return true;
162 }
163 }
164 // maximum width
165 if (',' == *sz) {
166 num = ++sz;
167 n = svto_radix<10>(num);
168 if (num.size() < sz.size()) {
169 _max = static_cast<decltype(_max)>(n);
170 sz = num;
171 if (!sz.size()) {
172 return true;
173 }
174 } else {
175 throw std::invalid_argument("Maximum width mark without width");
176 }
177 // Can only have a type indicator here if there was a max width.
178 if (is_type(*sz)) {
179 _type = *sz;
180 if (!(++sz).size()) {
181 return true;
182 }
183 }
184 }
185 }
186 }
187 return true;
188}
189
194bool
195Format::TextViewExtractor::parse(TextView &fmt, std::string_view &literal, std::string_view &specifier) {
196 TextView::size_type off = 0;
197
198 // Check for brace delimiters.
199 off = fmt.find_if([](char c) { return '{' == c || '}' == c; });
200 if (off == TextView::npos) {
201 // not found, it's a literal, ship it.
202 literal = fmt;
203 fmt.remove_prefix(literal.size());
204 return false;
205 }
206
207 // Processing for braces that don't enclose specifiers.
208 if (fmt.size() > off + 1) {
209 char c1 = fmt[off];
210 char c2 = fmt[off + 1];
211 if (c1 == c2) {
212 // double braces count as literals, but must tweak to output only 1 brace.
213 literal = fmt.take_prefix(off + 1);
214 return false;
215 } else if ('}' == c1) {
216 throw std::invalid_argument("Unopened } in format string.");
217 } else {
218 literal = std::string_view{fmt.data(), off};
219 fmt.remove_prefix(off + 1);
220 }
221 } else {
222 throw std::invalid_argument("Invalid trailing character in format string.");
223 }
224
225 if (fmt.size()) {
226 // Need to be careful, because an empty format is OK and it's hard to tell
227 // if take_prefix_at failed to find the delimiter or found it as the first
228 // byte.
229 off = fmt.find('}');
230 if (off == TextView::npos) {
231 throw std::invalid_argument("BWFormat: Unclosed { in format string");
232 }
233 specifier = fmt.take_prefix(off);
234 return true;
235 }
236 return false;
237}
238
239bool
240Format::TextViewExtractor::operator()(std::string_view &literal_v, Spec &spec) {
241 if (!_fmt.empty()) {
242 std::string_view spec_v;
243 if (parse(_fmt, literal_v, spec_v)) {
244 return spec.parse(spec_v);
245 }
246 }
247 return false;
248}
249
250bool
251Format::FormatExtractor::operator()(std::string_view &literal_v, swoc::bwf::Spec &spec) {
252 literal_v = {};
253 if (_idx < int(_fmt.size()) && _fmt[_idx]._type == Spec::LITERAL_TYPE) {
254 literal_v = _fmt[_idx++]._ext;
255 }
256 if (_idx < int(_fmt.size()) && _fmt[_idx]._type != Spec::LITERAL_TYPE) {
257 spec = _fmt[_idx++];
258 return true;
259 }
260 return false;
261}
262
263void
264Err_Bad_Arg_Index(BufferWriter &w, int i, size_t n) {
265 static const Format fmt{"{{BAD_ARG_INDEX:{} of {}}}"sv};
266 w.print(fmt, i, n);
267}
268
275void
277 size_t extent = aux.extent();
278 size_t min = spec._min;
279 if (extent < min) {
280 size_t delta = min - extent;
281 size_t left_delta = 0, right_delta = delta; // left justify values
282 if (Spec::Align::RIGHT == spec._align) {
283 left_delta = delta;
284 right_delta = 0;
285 } else if (Spec::Align::CENTER == spec._align) {
286 left_delta = delta / 2;
287 right_delta = (delta + 1) / 2;
288 }
289 if (left_delta > 0) {
290 size_t work_area = extent + left_delta;
291 aux.commit(left_delta); // cover work area.
292 aux.copy(left_delta, 0, extent); // move to create space for left fill.
293 aux.discard(work_area); // roll back to write the left fill.
294 for (int i = left_delta; i > 0; --i) {
295 aux.write(spec._fill);
296 }
297 aux.commit(extent);
298 }
299 for (int i = right_delta; i > 0; --i) {
300 aux.write(spec._fill);
301 }
302
303 } else {
304 size_t max = spec._max;
305 if (max < extent) {
306 aux.discard(extent - max);
307 }
308 }
309}
310
311// Conversions from remainder to character, in upper and lower case versions.
312// Really only useful for hexadecimal currently.
313namespace {
314char UPPER_DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
315char LOWER_DIGITS[] = "0123456789abcdefghijklmnopqrstuvwxyz";
316static const std::array<uint64_t, 11> POWERS_OF_TEN = {
317 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000}
318};
319} // namespace
320
325template <size_t RADIX>
326size_t
327To_Radix(uintmax_t n, char *buff, size_t width, char *digits) {
328 static_assert(1 < RADIX && RADIX <= 36, "RADIX must be in the range 2..36");
329 char *out = buff + width;
330 if (n) {
331 while (n) {
332 *--out = digits[n % RADIX];
333 n /= RADIX;
334 }
335 } else {
336 *--out = '0';
337 }
338 return (buff + width) - out;
339}
340
351template <typename F>
352void
353Write_Aligned(BufferWriter &w, F const &f, Spec::Align align, int width, char fill, char neg) {
354 switch (align) {
356 if (neg) {
357 w.write(neg);
358 }
359 f();
360 while (width-- > 0) {
361 w.write(fill);
362 }
363 break;
365 while (width-- > 0) {
366 w.write(fill);
367 }
368 if (neg) {
369 w.write(neg);
370 }
371 f();
372 break;
374 for (int i = width / 2; i > 0; --i) {
375 w.write(fill);
376 }
377 if (neg) {
378 w.write(neg);
379 }
380 f();
381 for (int i = (width + 1) / 2; i > 0; --i) {
382 w.write(fill);
383 }
384 break;
386 if (neg) {
387 w.write(neg);
388 }
389 while (width-- > 0) {
390 w.write(fill);
391 }
392 f();
393 break;
394 default:
395 if (neg) {
396 w.write(neg);
397 }
398 f();
399 break;
400 }
401}
402
404Format_Integer(BufferWriter &w, Spec const &spec, uintmax_t i, bool neg_p) {
405 size_t n = 0;
406 int width = static_cast<int>(spec._min); // amount left to fill.
407 char neg = 0;
408 char prefix1 = spec._radix_lead_p ? '0' : 0;
409 char prefix2 = 0;
410 char buff[std::numeric_limits<uintmax_t>::digits + 1];
411
412 if (spec._sign != Spec::SIGN_NEVER) {
413 if (neg_p) {
414 neg = '-';
415 } else if (spec._sign == Spec::SIGN_ALWAYS) {
416 neg = spec._sign;
417 }
418 }
419
420 switch (spec._type) {
421 case 'x':
422 prefix2 = 'x';
423 n = bwf::To_Radix<16>(i, buff, sizeof(buff), bwf::LOWER_DIGITS);
424 break;
425 case 'X':
426 prefix2 = 'X';
427 n = bwf::To_Radix<16>(i, buff, sizeof(buff), bwf::UPPER_DIGITS);
428 break;
429 case 'b':
430 prefix2 = 'b';
431 n = bwf::To_Radix<2>(i, buff, sizeof(buff), bwf::LOWER_DIGITS);
432 break;
433 case 'B':
434 prefix2 = 'B';
435 n = bwf::To_Radix<2>(i, buff, sizeof(buff), bwf::UPPER_DIGITS);
436 break;
437 case 'o':
438 n = bwf::To_Radix<8>(i, buff, sizeof(buff), bwf::LOWER_DIGITS);
439 break;
440 default:
441 prefix1 = 0;
442 n = bwf::To_Radix<10>(i, buff, sizeof(buff), bwf::LOWER_DIGITS);
443 break;
444 }
445 // Clip fill width by stuff that's already committed to be written.
446 if (neg) {
447 --width;
448 }
449 if (prefix1) {
450 --width;
451 if (prefix2) {
452 --width;
453 }
454 }
455 width -= static_cast<int>(n);
456 std::string_view digits{buff + sizeof(buff) - n, n};
457
458 if (spec._align == Spec::Align::SIGN) { // custom for signed case because
459 // prefix and digits are seperated.
460 if (neg) {
461 w.write(neg);
462 }
463 if (prefix1) {
464 w.write(prefix1);
465 if (prefix2) {
466 w.write(prefix2);
467 }
468 }
469 while (width-- > 0) {
470 w.write(spec._fill);
471 }
472 w.write(digits);
473 } else { // use generic Write_Aligned
475 w,
476 [&]() {
477 if (prefix1) {
478 w.write(prefix1);
479 if (prefix2) {
480 w.write(prefix2);
481 }
482 }
483 w.write(digits);
484 },
485 spec._align, width, spec._fill, neg);
486 }
487 return w;
488}
489
500Format_Float(BufferWriter &w, Spec const &spec, double f, bool negative_p) {
501 static const std::string_view infinity_bwf{"Inf"};
502 static const std::string_view nan_bwf{"NaN"};
503 static const std::string_view zero_bwf{"0"};
504 static const std::string_view subnormal_bwf{"subnormal"};
505 static const std::string_view unknown_bwf{"unknown float"};
506
507 // Handle floating values that are not normal
508 if (!std::isnormal(f)) {
509 std::string_view unnormal;
510 switch (std::fpclassify(f)) {
511 case FP_INFINITE:
512 unnormal = infinity_bwf;
513 break;
514 case FP_NAN:
515 unnormal = nan_bwf;
516 break;
517 case FP_ZERO:
518 unnormal = zero_bwf;
519 break;
520 case FP_SUBNORMAL:
521 unnormal = subnormal_bwf;
522 break;
523 default:
524 unnormal = unknown_bwf;
525 }
526
527 w.write(unnormal);
528 return w;
529 }
530
531 auto whole_part = static_cast<uint64_t>(f);
532 if (whole_part == f || spec._prec == 0) { // integral
533 return Format_Integer(w, spec, whole_part, negative_p);
534 }
535
536 static constexpr char dec = '.';
537 double frac = NAN;
538 size_t l = 0;
539 size_t r = 0;
540 char whole[std::numeric_limits<double>::digits10 + 1];
541 char fraction[std::numeric_limits<double>::digits10 + 1];
542 char neg = 0;
543 int width = static_cast<int>(spec._min); // amount left to fill.
544 unsigned int precision = (spec._prec == Spec::DEFAULT._prec) ? 2 : spec._prec; // default precision 2
545
546 frac = f - whole_part; // split the number
547
548 if (negative_p) {
549 neg = '-';
550 } else if (spec._sign != '-') {
551 neg = spec._sign;
552 }
553
554 // Shift the floating point based on the precision. Used to convert
555 // trailing fraction into an integer value.
556 uint64_t shift = 0;
557 if (precision < POWERS_OF_TEN.size()) {
558 shift = POWERS_OF_TEN[precision];
559 } else { // not precomputed.
560 shift = POWERS_OF_TEN.back();
561 for (precision -= (POWERS_OF_TEN.size() - 1); precision > 0; --precision) {
562 shift *= 10;
563 }
564 }
565
566 auto frac_part = static_cast<uint64_t>(frac * shift + 0.5 /* rounding */);
567
568 l = bwf::To_Radix<10>(whole_part, whole, sizeof(whole), bwf::LOWER_DIGITS);
569 r = bwf::To_Radix<10>(frac_part, fraction, sizeof(fraction), bwf::LOWER_DIGITS);
570
571 // Clip fill width
572 if (neg) {
573 --width;
574 }
575 width -= static_cast<int>(l);
576 --width; // '.'
577 width -= static_cast<int>(r);
578
579 std::string_view whole_digits{whole + sizeof(whole) - l, l};
580 std::string_view frac_digits{fraction + sizeof(fraction) - r, r};
581
583 w,
584 [&]() {
585 w.write(whole_digits);
586 w.write(dec);
587 w.write(frac_digits);
588 },
589 spec._align, width, spec._fill, neg);
590
591 return w;
592}
593
594void
595Format_As_Hex(BufferWriter &w, std::string_view view, const char *digits) {
596 const char *ptr = view.data();
597 for (auto n = view.size(); n > 0; --n) {
598 char c = *ptr++;
599 w.write(digits[(c >> 4) & 0xF]);
600 w.write(digits[c & 0xf]);
601 }
602}
603
606 Spec lit_spec;
607 int arg_idx = 0;
608 auto ex{bind(fmt)};
609 std::string_view literal_v;
610
611 lit_spec._type = Spec::LITERAL_TYPE;
612
613 while (ex) {
614 Spec spec;
615 bool spec_p = ex(literal_v, spec);
616
617 if (literal_v.size()) {
618 lit_spec._ext = literal_v;
619 _items.emplace_back(lit_spec);
620 }
621
622 if (spec_p) {
623 if (spec._name.size() == 0) { // no name provided, use implicit index.
624 spec._idx = arg_idx++;
625 }
626 if (spec._idx >= 0) {
627 ++arg_idx;
628 }
629 _items.emplace_back(spec);
630 }
631 }
632}
633
634bool
636 for (auto const &spec : _items) {
637 if (Spec::LITERAL_TYPE != spec._type) {
638 return false;
639 }
640 }
641 return true;
642}
643
644NameBinding::~NameBinding() = default;
645
646} // namespace bwf
647
649bwformat(BufferWriter &w, bwf::Spec const &spec, std::string_view sv) {
650 auto width = int(spec._min); // amount to fill.
651 if (spec._prec > 0) {
652 sv = sv.substr(0, spec._prec);
653 }
654 if ('x' == spec._type || 'X' == spec._type) {
655 return bwformat(w, spec, bwf::HexDump(sv.data(), sv.size()));
656 } else if ('s' == spec._type) {
657 bwformat(w, spec, transform_view_of(&tolower, sv));
658 } else if ('S' == spec._type) {
659 bwformat(w, spec, transform_view_of(&toupper, sv));
660 } else {
661 width -= sv.size();
663 w, [&w, &sv]() { w.write(sv); }, spec._align, width, spec._fill, 0);
664 }
665 return w;
666}
667
668// Generic pointer formatting
670bwformat(BufferWriter &w, bwf::Spec const &spec, const void *ptr) {
671 using namespace swoc::literals;
672 bwf::Spec ptr_spec{spec};
673 ptr_spec._radix_lead_p = true;
674
675 if (ptr == nullptr) {
676 if (spec._type == 's' || spec._type == 'S') {
677 ptr_spec._type = bwf::Spec::DEFAULT_TYPE;
678 ptr_spec._ext = ""_sv; // clear any extension.
679 return bwformat(w, spec, spec._type == 's' ? "null"_sv : "NULL"_sv);
680 } else if (spec._type == bwf::Spec::DEFAULT_TYPE) {
681 return w; // print nothing if there is no format character override.
682 }
683 }
684
685 if (ptr_spec._type == bwf::Spec::DEFAULT_TYPE || ptr_spec._type == 'p') {
686 ptr_spec._type = 'x'; // if default or 'p;, switch to lower hex.
687 } else if (ptr_spec._type == 'P') {
688 ptr_spec._type = 'X'; // P means upper hex, overriding other specializations.
689 }
690 return bwf::Format_Integer(w, ptr_spec, reinterpret_cast<intptr_t>(ptr), false);
691}
692// doc end
693
695bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::HexDump const &hex) {
696 char fmt_type = spec._type;
697 const char *digits = bwf::UPPER_DIGITS;
698
699 if ('X' != fmt_type) {
700 fmt_type = 'x';
701 digits = bwf::LOWER_DIGITS;
702 }
703
704 int width = int(spec._min) - hex._view.size() * 2; // amount left to fill.
705 if (spec._radix_lead_p) {
706 w.write('0');
707 w.write(fmt_type);
708 width -= 2;
709 }
711 w, [&w, &hex, digits]() { bwf::Format_As_Hex(w, hex._view, digits); }, spec._align, width, spec._fill, 0);
712 return w;
713}
714
716bwformat(BufferWriter &w, bwf::Spec const &spec, MemSpan<void const> const &span) {
717 if ('x' == spec._type || 'X' == spec._type) {
718 const char *digits = ('X' == spec._type) ? bwf::UPPER_DIGITS : bwf::LOWER_DIGITS;
719 size_t block = spec._prec > 0 ? spec._prec : span.size();
720 TextView view{span.rebind<char const>()};
721 bool space_p = false;
722 while (view) {
723 if (space_p) {
724 w.write(' ');
725 }
726 space_p = true;
727 if (spec._radix_lead_p) {
728 w.write('0').write(digits[33]);
729 }
730 bwf::Format_As_Hex(w, view.prefix(block), digits);
731 view.remove_prefix(block);
732 }
733 } else {
734 static const bwf::Format default_fmt{"{:#x}@{:p}"};
735 w.print(default_fmt, span.size(), span.data());
736 }
737 return w;
738}
739
740std::ostream &
741FixedBufferWriter::operator>>(std::ostream &s) const {
742 return s << this->view();
743}
744
745namespace {
746// Hand rolled, might not be totally compliant everywhere, but probably close
747// enough. The long string will be locally accurate. Clang requires the double
748// braces. Why, Turing only knows.
749static const std::array<std::string_view, 134> ERRNO_SHORT_NAME = {
750 {
751 "SUCCESS", "EPERM",
752 "ENOENT", "ESRCH",
753 "EINTR", "EIO",
754 "ENXIO", "E2BIG ",
755 "ENOEXEC", "EBADF",
756 "ECHILD", "EAGAIN",
757 "ENOMEM", "EACCES",
758 "EFAULT", "ENOTBLK",
759 "EBUSY", "EEXIST",
760 "EXDEV", "ENODEV",
761 "ENOTDIR", "EISDIR",
762 "EINVAL", "ENFILE",
763 "EMFILE", "ENOTTY",
764 "ETXTBSY", "EFBIG",
765 "ENOSPC", "ESPIPE",
766 "EROFS", "EMLINK",
767 "EPIPE", "EDOM",
768 "ERANGE", "EDEADLK",
769 "ENAMETOOLONG", "ENOLCK",
770 "ENOSYS", "ENOTEMPTY",
771 "ELOOP", "EWOULDBLOCK",
772 "ENOMSG", "EIDRM",
773 "ECHRNG", "EL2NSYNC",
774 "EL3HLT", "EL3RST",
775 "ELNRNG", "EUNATCH",
776 "ENOCSI", "EL2HTL",
777 "EBADE", "EBADR",
778 "EXFULL", "ENOANO",
779 "EBADRQC", "EBADSLT",
780 "EDEADLOCK", "EBFONT",
781 "ENOSTR", "ENODATA",
782 "ETIME", "ENOSR",
783 "ENONET", "ENOPKG",
784 "EREMOTE", "ENOLINK",
785 "EADV", "ESRMNT",
786 "ECOMM", "EPROTO",
787 "EMULTIHOP", "EDOTDOT",
788 "EBADMSG", "EOVERFLOW",
789 "ENOTUNIQ", "EBADFD",
790 "EREMCHG", "ELIBACC",
791 "ELIBBAD", "ELIBSCN",
792 "ELIBMAX", "ELIBEXEC",
793 "EILSEQ", "ERESTART",
794 "ESTRPIPE", "EUSERS",
795 "ENOTSOCK", "EDESTADDRREQ",
796 "EMSGSIZE", "EPROTOTYPE",
797 "ENOPROTOOPT", "EPROTONOSUPPORT",
798 "ESOCKTNOSUPPORT", "EOPNOTSUPP",
799 "EPFNOSUPPORT", "EAFNOSUPPORT",
800 "EADDRINUSE", "EADDRNOTAVAIL",
801 "ENETDOWN", "ENETUNREACH",
802 "ENETRESET", "ECONNABORTED",
803 "ECONNRESET", "ENOBUFS",
804 "EISCONN", "ENOTCONN",
805 "ESHUTDOWN", "ETOOMANYREFS",
806 "ETIMEDOUT", "ECONNREFUSED",
807 "EHOSTDOWN", "EHOSTUNREACH",
808 "EALREADY", "EINPROGRESS",
809 "ESTALE", "EUCLEAN",
810 "ENOTNAM", "ENAVAIL",
811 "EISNAM", "EREMOTEIO",
812 "EDQUOT", "ENOMEDIUM",
813 "EMEDIUMTYPE", "ECANCELED",
814 "ENOKEY", "EKEYEXPIRED",
815 "EKEYREVOKED", "EKEYREJECTED",
816 "EOWNERDEAD", "ENOTRECOVERABLE",
817 "ERFKILL", "EHWPOISON",
818 }
819};
820static constexpr DiscreteRange<unsigned> ERRNO_RANGE{0, ERRNO_SHORT_NAME.size() - 1};
821// This provides convenient safe access to the errno short name array.
822auto errno_short_name = [](unsigned n) { return ERRNO_RANGE.contains(n) ? ERRNO_SHORT_NAME[n] : "Unknown"sv; };
823} // namespace
824
825BufferWriter &
826bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Errno const &e) {
827 static const bwf::Format number_fmt{"[{}]"sv}; // numeric value format.
828
829 if (spec.has_numeric_type()) { // if numeric type, print just the numeric part
830 w.print(number_fmt, e._e);
831 } else {
832 TextView ext{spec._ext};
833 bool short_p = false;
834 if (ext.empty() || ext.npos != ext.find('s')) {
835 w.write(errno_short_name(e._e));
836 short_p = true;
837 }
838 if (ext.empty() || ext.npos != ext.find('l')) {
839 if (short_p) {
840 w.write(": ");
841 }
842 w.write(TextView(strerror(e._e), TextView::npos));
843 }
844 if (spec._type != 's' && spec._type != 'S') {
845 w.write(' ');
846 w.print(number_fmt, e._e);
847 }
848 }
849 return w;
850}
851
852bwf::Date::Date(std::string_view fmt) : _epoch(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())), _fmt(fmt) {}
853
855bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Date const &date) {
856 if (spec.has_numeric_type()) {
857 bwformat(w, spec, date._epoch);
858 } else {
859 struct tm t {};
860 auto r = w.remaining();
861 size_t n{0};
862 // Verify @a fmt is null terminated, even outside the bounds of the view.
863 if (date._fmt.data()[date._fmt.size() - 1] != 0 && date._fmt.data()[date._fmt.size()] != 0) {
864 throw(std::invalid_argument{"BWF Date String is not null terminated."});
865 }
866 // Get the time, GMT or local if specified.
867 if (spec._ext == "local"sv) {
868 localtime_r(&date._epoch, &t);
869 } else {
870 gmtime_r(&date._epoch, &t);
871 }
872 // Try a direct write, faster if it works.
873 if (r > 0) {
874 n = strftime(w.aux_data(), r, date._fmt.data(), &t);
875 }
876 if (n > 0) {
877 w.commit(n);
878 } else {
879 // Direct write didn't work. Unfortunately need to write to a temporary
880 // buffer or the sizing isn't correct if @a w is clipped because @c
881 // strftime returns 0 if the buffer isn't large enough.
882 char buff[256]; // hope for the best - no real way to resize appropriately on failure.
883 n = strftime(buff, sizeof(buff), date._fmt.data(), &t);
884 w.write(buff, n);
885 }
886 }
887 return w;
888}
889
891bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Pattern const &pattern) {
892 if (!pattern._text.empty()) { // If there's no text, no point in looping.
893 auto limit = std::min<size_t>(spec._max, pattern._text.size() * pattern._n);
894 decltype(limit) n = 0;
895 while (n < limit) {
896 w.write(pattern._text);
897 n += pattern._text.size();
898 }
899 }
900 return w;
901}
902
904bwformat(BufferWriter &w, bwf::Spec const &spec, std::error_code const &ec) {
905 static const auto G_CAT = &std::generic_category();
906 static const auto S_CAT = &std::system_category();
907
908 // This provides convenient safe access to the errno short name array.
909 static const swoc::bwf::Format number_fmt{"[{}]"_sv}; // numeric value format.
910 if (spec.has_numeric_type()) { // if numeric type, print just the numeric part.
911 bwformat(w, spec, ec.value());
912 } else {
913 if ((&ec.category() == G_CAT || &ec.category() == S_CAT) && swoc::ERRNO_RANGE.contains(ec.value())) {
914 bwformat(w, spec, swoc::ERRNO_SHORT_NAME[ec.value()]);
915 } else {
916 w.write(ec.message());
917 }
918 if (spec._type != 's' && spec._type != 'S') {
919 w.write(' ').write('[').format(spec, ec.value()).write(']');
920 }
921 }
922 return w;
923}
924
925BufferWriter &
926bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::UnHex const &obj) {
927 auto span{obj._span};
928 size_t limit = spec._max;
929 while (span.size() >= 2 && limit--) {
930 auto b = svto_radix<16>(span.clip_prefix(2).rebind<char const>());
931 w.write(b);
932 }
933 return w;
934}
935
936}} // namespace swoc::SWOC_VERSION_NS
937
938namespace std {
939ostream &
940operator<<(ostream &s, swoc::FixedBufferWriter &w) {
941 return s << w.view();
942}
943
944} // namespace std
uintmax_t svto_radix(TextView &src)
Definition TextView.h:1059
size_t To_Radix(uintmax_t n, char *buff, size_t width, char *digits)
Definition bw_format.cc:327
BufferWriter & Format_Float(BufferWriter &w, Spec const &spec, double f, bool negative_p)
Definition bw_format.cc:500
ExternalNames & Global_Names()
Definition bw_format.cc:33
BufferWriter & Format_Integer(BufferWriter &w, Spec const &spec, uintmax_t i, bool neg_p)
Definition bw_format.cc:404
void Format_As_Hex(BufferWriter &w, std::string_view view, const char *digits)
Definition bw_format.cc:595
void Err_Bad_Arg_Index(BufferWriter &w, int i, size_t n)
Internal error / reporting message generators.
Definition bw_format.cc:264
void Write_Aligned(BufferWriter &w, F const &f, Spec::Align align, int width, char fill, char neg)
Definition bw_format.cc:353
void Adjust_Alignment(BufferWriter &aux, Spec const &spec)
Definition bw_format.cc:276
virtual bool commit(size_t n)=0
virtual BufferWriter & copy(size_t dst, size_t src, size_t n)=0
BufferWriter & print(const TextView &fmt, Args &&...args)
Definition bwf_base.h:927
BufferWriter & format(bwf::Spec const &spec, T &&t)
Definition bwf_base.h:1346
virtual BufferWriter & discard(size_t n)=0
virtual size_t extent() const =0
size_t remaining() const
virtual BufferWriter & write(char c)=0
virtual char * aux_data()
swoc::TextView view() const
std::ostream & operator>>(std::ostream &stream) const override
Output the buffer contents to the stream.
Definition bw_format.cc:741
T * data() const
Definition MemSpan.h:1154
MemSpan< U > rebind() const
Definition MemSpan.h:1304
constexpr size_t size() const
Number of elements in the span.
Definition MemSpan.h:1178
self_type & remove_prefix(size_t n)
self_type take_prefix_at(char c)
self_type take_prefix(size_t n)
constexpr value_type const * data() const noexcept
size_t find_if(F const &pred) const
Get the offset of the first character for which pred is true.
virtual ~NameBinding()
Force virtual destructor.
STL namespace.
For template deduction guides.
Definition ArenaWriter.cc:9
uintmax_t svto_radix(TextView &src)
Definition TextView.h:1059
BufferWriter & bwformat(BufferWriter &w, bwf::Spec const &spec, std::string_view sv)
Definition bw_format.cc:649
TransformView< X, V > transform_view_of(X const &xf, V const &src)
Definition TextView.h:1933
MemSpan< T > const & memset(MemSpan< T > const &dst, T const &value)
Definition MemSpan.h:1022
static const self_type DEFAULT
Global default instance for use in situations where a format specifier isn't available.
Definition bwf_base.h:80
Date(time_t t, std::string_view fmt=DEFAULT_FORMAT)
Definition bwf_ex.h:56
time_t _epoch
The time.
Definition bwf_ex.h:48
std::string_view _fmt
Data format.
Definition bwf_ex.h:49
int _e
Errno value.
Definition bwf_ex.h:35
MemSpan< Spec const > _fmt
Parsed format string.
Definition bwf_base.h:188
bool operator()(std::string_view &literal_v, Spec &spec)
Definition bw_format.cc:251
TextView _fmt
Format string.
Definition bwf_base.h:156
bool operator()(std::string_view &literal_v, Spec &spec)
Definition bw_format.cc:240
static bool parse(TextView &fmt, std::string_view &literal, std::string_view &specifier)
Definition bw_format.cc:195
bool is_literal() const
Definition bw_format.cc:635
Container _items
Items from format string.
Definition bwf_base.h:209
static TextViewExtractor bind(TextView fmt)
Wrap the format string in an extractor.
Definition bwf_base.h:527
Format()=default
Empty format.
std::string_view _view
A view of the memory to dump.
Definition bwf_base.h:1286
std::string_view _text
output text.
Definition bwf_ex.h:26
static constexpr uint8_t UPPER_TYPE_CHAR
Upper case flag.
Definition bwf_base.h:118
static constexpr uint8_t TYPE_CHAR
A valid type character.
Definition bwf_base.h:117
static constexpr uint8_t NUMERIC_TYPE_CHAR
Numeric output.
Definition bwf_base.h:119
static constexpr uint8_t SIGN_CHAR
Is sign character.
Definition bwf_base.h:120
uint8_t _data[0x100]
Flag storage, indexed by character value.
Definition bwf_base.h:114
bool parse(TextView fmt)
Parse a format specification.
Definition bw_format.cc:72
bool _radix_lead_p
Print leading radix indication.
Definition bwf_base.h:70
int _prec
Precision.
Definition bwf_base.h:73
static constexpr char SIGN_NEG
Print a sign character only for negative values (default).
Definition bwf_base.h:47
unsigned int _max
Maximum width.
Definition bwf_base.h:74
static constexpr char DEFAULT_TYPE
Default format type.
Definition bwf_base.h:40
unsigned int _min
Minimum width.
Definition bwf_base.h:72
enum swoc::bwf::Spec::Align _align
Output field alignment.
static bool is_type(char c)
Validate c is a specifier type indicator.
Definition bwf_base.h:492
constexpr Spec()
Constructor a default instance.
Definition bwf_base.h:50
int _idx
Positional "name" of the specification.
Definition bwf_base.h:75
Align align_of(char c)
Validate character is alignment character and return the appropriate enum value.
Definition bwf_base.h:482
bool is_sign(char c)
Validate is sign indicator.
Definition bwf_base.h:487
Align
Flag for how to align the output inside a limited width field.
Definition bwf_base.h:62
@ RIGHT
Right alignment '>'.
Definition bwf_base.h:65
@ SIGN
Align plus/minus sign before numeric fill. '='.
Definition bwf_base.h:67
@ LEFT
Left alignment '<'.
Definition bwf_base.h:64
@ NONE
No alignment.
Definition bwf_base.h:63
@ CENTER
Center alignment '^'.
Definition bwf_base.h:66
static constexpr char SIGN_ALWAYS
Always print a sign character.
Definition bwf_base.h:45
char _fill
Fill character.
Definition bwf_base.h:59
static const self_type DEFAULT
Global default instance for use in situations where a format specifier isn't available.
Definition bwf_base.h:80
static constexpr char SIGN_NEVER
Never print a sign character.
Definition bwf_base.h:46
static constexpr char LITERAL_TYPE
Internal type to mark a literal.
Definition bwf_base.h:42
char _type
Type / radix indicator.
Definition bwf_base.h:69
bool has_numeric_type() const
Check if the type in this is numeric.
Definition bwf_base.h:507
std::string_view _name
Name of the specification.
Definition bwf_base.h:76
std::string_view _ext
Extension if provided.
Definition bwf_base.h:77
MemSpan< void const > _span
Source span.
Definition bwf_ex.h:224