24using namespace std::literals;
30namespace swoc {
inline namespace SWOC_VERSION_NS {
40#pragma GCC diagnostic ignored "-Wchar-subscripts"
80 _idx =
static_cast<decltype(
_idx)
>(n);
91 throw std::invalid_argument(
"Fill URI encoding without 2 hex characters and align mark");
94 throw std::invalid_argument(
"Fill URI without alignment mark");
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");
100 _fill = isdigit(d0) ? d0 -
'0' : tolower(d0) -
'a' + 10;
101 _fill += (isdigit(d1) ? d1 -
'0' : tolower(d1) -
'a' + 10) << 4;
115 if (!(++sz).size()) {
122 if (!(++sz).size()) {
136 if (num.size() < sz.size()) {
137 _min =
static_cast<decltype(
_min)
>(n);
147 if (num.size() < sz.size()) {
154 throw std::invalid_argument(
"Precision mark without precision");
160 if (!(++sz).size()) {
168 if (num.size() < sz.size()) {
169 _max =
static_cast<decltype(
_max)
>(n);
175 throw std::invalid_argument(
"Maximum width mark without width");
180 if (!(++sz).size()) {
196 TextView::size_type off = 0;
199 off = fmt.
find_if([](
char c) {
return '{' == c ||
'}' == c; });
200 if (off == TextView::npos) {
208 if (fmt.size() > off + 1) {
210 char c2 = fmt[off + 1];
215 }
else if (
'}' == c1) {
216 throw std::invalid_argument(
"Unopened } in format string.");
218 literal = std::string_view{fmt.
data(), off};
222 throw std::invalid_argument(
"Invalid trailing character in format string.");
230 if (off == TextView::npos) {
231 throw std::invalid_argument(
"BWFormat: Unclosed { in format string");
242 std::string_view spec_v;
244 return spec.
parse(spec_v);
265 static const Format fmt{
"{{BAD_ARG_INDEX:{} of {}}}"sv};
277 size_t extent = aux.
extent();
278 size_t min = spec.
_min;
280 size_t delta = min - extent;
281 size_t left_delta = 0, right_delta = delta;
286 left_delta = delta / 2;
287 right_delta = (delta + 1) / 2;
289 if (left_delta > 0) {
290 size_t work_area = extent + left_delta;
292 aux.
copy(left_delta, 0, extent);
294 for (
int i = left_delta; i > 0; --i) {
299 for (
int i = right_delta; i > 0; --i) {
304 size_t max = spec.
_max;
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}
325template <
size_t RADIX>
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;
332 *--out = digits[n % RADIX];
338 return (buff + width) - out;
360 while (width-- > 0) {
365 while (width-- > 0) {
374 for (
int i = width / 2; i > 0; --i) {
381 for (
int i = (width + 1) / 2; i > 0; --i) {
389 while (width-- > 0) {
406 int width =
static_cast<int>(spec.
_min);
410 char buff[std::numeric_limits<uintmax_t>::digits + 1];
420 switch (spec.
_type) {
455 width -=
static_cast<int>(n);
456 std::string_view digits{buff +
sizeof(buff) - n, n};
469 while (width-- > 0) {
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"};
508 if (!std::isnormal(f)) {
509 std::string_view unnormal;
510 switch (std::fpclassify(f)) {
512 unnormal = infinity_bwf;
521 unnormal = subnormal_bwf;
524 unnormal = unknown_bwf;
531 auto whole_part =
static_cast<uint64_t
>(f);
532 if (whole_part == f || spec.
_prec == 0) {
536 static constexpr char dec =
'.';
540 char whole[std::numeric_limits<double>::digits10 + 1];
541 char fraction[std::numeric_limits<double>::digits10 + 1];
543 int width =
static_cast<int>(spec.
_min);
546 frac = f - whole_part;
550 }
else if (spec.
_sign !=
'-') {
557 if (precision < POWERS_OF_TEN.size()) {
558 shift = POWERS_OF_TEN[precision];
560 shift = POWERS_OF_TEN.back();
561 for (precision -= (POWERS_OF_TEN.size() - 1); precision > 0; --precision) {
566 auto frac_part =
static_cast<uint64_t
>(frac * shift + 0.5 );
575 width -=
static_cast<int>(l);
577 width -=
static_cast<int>(r);
579 std::string_view whole_digits{whole +
sizeof(whole) - l, l};
580 std::string_view frac_digits{fraction +
sizeof(fraction) - r, r};
585 w.
write(whole_digits);
587 w.
write(frac_digits);
596 const char *ptr = view.data();
597 for (
auto n = view.size(); n > 0; --n) {
599 w.
write(digits[(c >> 4) & 0xF]);
600 w.
write(digits[c & 0xf]);
609 std::string_view literal_v;
615 bool spec_p = ex(literal_v, spec);
617 if (literal_v.size()) {
618 lit_spec.
_ext = literal_v;
619 _items.emplace_back(lit_spec);
623 if (spec.
_name.size() == 0) {
624 spec.
_idx = arg_idx++;
626 if (spec.
_idx >= 0) {
629 _items.emplace_back(spec);
636 for (
auto const &spec :
_items) {
650 auto width = int(spec.
_min);
651 if (spec.
_prec > 0) {
652 sv = sv.substr(0, spec.
_prec);
656 }
else if (
's' == spec.
_type) {
658 }
else if (
'S' == spec.
_type) {
675 if (ptr ==
nullptr) {
678 ptr_spec._ext =
""_sv;
679 return bwformat(w, spec, spec.
_type ==
's' ?
"null"_sv :
"NULL"_sv);
686 ptr_spec._type =
'x';
687 }
else if (ptr_spec._type ==
'P') {
688 ptr_spec._type =
'X';
696 char fmt_type = spec.
_type;
697 const char *digits = bwf::UPPER_DIGITS;
699 if (
'X' != fmt_type) {
701 digits = bwf::LOWER_DIGITS;
704 int width = int(spec.
_min) - hex.
_view.size() * 2;
718 const char *digits = (
'X' == spec.
_type) ? bwf::UPPER_DIGITS : bwf::LOWER_DIGITS;
721 bool space_p =
false;
731 view.remove_prefix(block);
734 static const bwf::Format default_fmt{
"{:#x}@{:p}"};
742 return s << this->
view();
749static const std::array<std::string_view, 134> ERRNO_SHORT_NAME = {
769 "ENAMETOOLONG",
"ENOLCK",
770 "ENOSYS",
"ENOTEMPTY",
771 "ELOOP",
"EWOULDBLOCK",
773 "ECHRNG",
"EL2NSYNC",
779 "EBADRQC",
"EBADSLT",
780 "EDEADLOCK",
"EBFONT",
784 "EREMOTE",
"ENOLINK",
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",
810 "ENOTNAM",
"ENAVAIL",
811 "EISNAM",
"EREMOTEIO",
812 "EDQUOT",
"ENOMEDIUM",
813 "EMEDIUMTYPE",
"ECANCELED",
814 "ENOKEY",
"EKEYEXPIRED",
815 "EKEYREVOKED",
"EKEYREJECTED",
816 "EOWNERDEAD",
"ENOTRECOVERABLE",
817 "ERFKILL",
"EHWPOISON",
820static constexpr DiscreteRange<unsigned> ERRNO_RANGE{0, ERRNO_SHORT_NAME.size() - 1};
822auto errno_short_name = [](
unsigned n) {
return ERRNO_RANGE.contains(n) ? ERRNO_SHORT_NAME[n] :
"Unknown"sv; };
833 bool short_p =
false;
834 if (ext.empty() || ext.npos != ext.find(
's')) {
835 w.
write(errno_short_name(e.
_e));
838 if (ext.empty() || ext.npos != ext.find(
'l')) {
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."});
867 if (spec.
_ext ==
"local"sv) {
868 localtime_r(&date.
_epoch, &t);
870 gmtime_r(&date.
_epoch, &t);
883 n = strftime(buff,
sizeof(buff), date.
_fmt.data(), &t);
892 if (!pattern.
_text.empty()) {
893 auto limit = std::min<size_t>(spec.
_max, pattern.
_text.size() * pattern.
_n);
894 decltype(limit) n = 0;
897 n += pattern.
_text.size();
905 static const auto G_CAT = &std::generic_category();
906 static const auto S_CAT = &std::system_category();
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()]);
916 w.
write(ec.message());
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>());
940operator<<(ostream &s, swoc::FixedBufferWriter &w) {
941 return s << w.
view();
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)
BufferWriter & format(bwf::Spec const &spec, T &&t)
virtual BufferWriter & discard(size_t n)=0
virtual size_t extent() const =0
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.
MemSpan< U > rebind() const
constexpr size_t size() const
Number of elements in the span.
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.
For template deduction guides.
uintmax_t svto_radix(TextView &src)
BufferWriter & bwformat(BufferWriter &w, bwf::Spec const &spec, std::string_view sv)
TransformView< X, V > transform_view_of(X const &xf, V const &src)
MemSpan< T > const & memset(MemSpan< T > const &dst, T const &value)
static const self_type DEFAULT
Global default instance for use in situations where a format specifier isn't available.
Date(time_t t, std::string_view fmt=DEFAULT_FORMAT)
std::string_view _fmt
Data format.
std::string_view _view
A view of the memory to dump.
std::string_view _text
output text.
static constexpr uint8_t UPPER_TYPE_CHAR
Upper case flag.
static constexpr uint8_t TYPE_CHAR
A valid type character.
static constexpr uint8_t NUMERIC_TYPE_CHAR
Numeric output.
static constexpr uint8_t SIGN_CHAR
Is sign character.
uint8_t _data[0x100]
Flag storage, indexed by character value.
bool parse(TextView fmt)
Parse a format specification.
bool _radix_lead_p
Print leading radix indication.
static constexpr char SIGN_NEG
Print a sign character only for negative values (default).
unsigned int _max
Maximum width.
static constexpr char DEFAULT_TYPE
Default format type.
unsigned int _min
Minimum width.
enum swoc::bwf::Spec::Align _align
Output field alignment.
static bool is_type(char c)
Validate c is a specifier type indicator.
constexpr Spec()
Constructor a default instance.
int _idx
Positional "name" of the specification.
Align align_of(char c)
Validate character is alignment character and return the appropriate enum value.
bool is_sign(char c)
Validate is sign indicator.
Align
Flag for how to align the output inside a limited width field.
@ RIGHT
Right alignment '>'.
@ SIGN
Align plus/minus sign before numeric fill. '='.
@ LEFT
Left alignment '<'.
@ CENTER
Center alignment '^'.
static constexpr char SIGN_ALWAYS
Always print a sign character.
char _fill
Fill character.
static const self_type DEFAULT
Global default instance for use in situations where a format specifier isn't available.
static constexpr char SIGN_NEVER
Never print a sign character.
static constexpr char LITERAL_TYPE
Internal type to mark a literal.
char _type
Type / radix indicator.
bool has_numeric_type() const
Check if the type in this is numeric.
std::string_view _name
Name of the specification.
std::string_view _ext
Extension if provided.
MemSpan< void const > _span
Source span.