// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/utils/common.h" #include "td/utils/invoke.h" #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include #include namespace td { namespace format { /*** HexDump ***/ template struct HexDumpSize { const unsigned char *data; }; inline char hex_digit(int x) { return "0123456789abcdef"[x]; } template StringBuilder &operator<<(StringBuilder &builder, const HexDumpSize &dump) { const unsigned char *ptr = dump.data; // TODO: append unsafe for (std::size_t i = 0; i < size; i++) { int xy = ptr[is_reversed ? size - 1 - i : i]; int x = xy >> 4; int y = xy & 15; builder << hex_digit(x) << hex_digit(y); } return builder; } template struct HexDumpSlice { Slice slice; }; template StringBuilder &operator<<(StringBuilder &builder, const HexDumpSlice &dump) { const auto str = dump.slice; const auto size = str.size(); builder << '\n'; const std::size_t first_part_size = size % align; if (first_part_size) { builder << HexDumpSlice<1>{str.substr(0, first_part_size)} << '\n'; } for (std::size_t i = first_part_size; i < size; i += align) { builder << HexDumpSize{str.ubegin() + i}; if (((i / align) & 15) == 15 || i + align >= size) { builder << '\n'; } else { builder << ' '; } } return builder; } inline StringBuilder &operator<<(StringBuilder &builder, const HexDumpSlice<0> &dump) { auto size = dump.slice.size(); const uint8 *ptr = dump.slice.ubegin(); for (size_t i = 0; i < size; i++) { builder << HexDumpSize<1>{ptr + i}; } return builder; } template HexDumpSlice as_hex_dump(Slice slice) { return HexDumpSlice{slice}; } template HexDumpSlice as_hex_dump(MutableSlice slice) { return HexDumpSlice{slice}; } template HexDumpSlice as_hex_dump(const T &value) { return HexDumpSlice{Slice(&value, sizeof(value))}; } template HexDumpSize as_hex_dump(const T &value) { return HexDumpSize{reinterpret_cast(&value)}; } /*** Hex ***/ template struct Hex { const T &value; }; template Hex as_hex(const T &value) { return Hex{value}; } template StringBuilder &operator<<(StringBuilder &builder, const Hex &hex) { builder << "0x" << as_hex_dump(hex.value); return builder; } /*** Binary ***/ template struct Binary { const T &value; }; template Binary as_binary(const T &value) { return Binary{value}; } template StringBuilder &operator<<(StringBuilder &builder, const Binary &hex) { for (size_t i = 0; i < sizeof(T) * 8; i++) { builder << ((hex.value >> i) & 1 ? '1' : '0'); } return builder; } /*** Escaped ***/ struct Escaped { Slice str; }; inline StringBuilder &operator<<(StringBuilder &builder, const Escaped &escaped) { Slice str = escaped.str; for (unsigned char c : str) { if (c > 31 && c < 127 && c != '"' && c != '\\') { builder << static_cast(c); } else { const char *oct = "01234567"; builder << '\\' << oct[c >> 6] << oct[(c >> 3) & 7] << oct[c & 7]; } } return builder; } inline Escaped escaped(Slice slice) { return Escaped{slice}; } /*** Time to string ***/ struct Time { double seconds_; }; inline StringBuilder &operator<<(StringBuilder &logger, Time t) { struct NamedValue { const char *name; double value; }; static constexpr NamedValue durations[] = {{"ns", 1e-9}, {"us", 1e-6}, {"ms", 1e-3}, {"s", 1}}; static constexpr size_t durations_n = sizeof(durations) / sizeof(NamedValue); size_t i = 0; while (i + 1 < durations_n && t.seconds_ > 10 * durations[i + 1].value) { i++; } logger << StringBuilder::FixedDouble(t.seconds_ / durations[i].value, 1) << Slice(durations[i].name); return logger; } inline Time as_time(double seconds) { return Time{seconds}; } /*** Size to string ***/ struct Size { uint64 size_; }; inline StringBuilder &operator<<(StringBuilder &logger, Size t) { struct NamedValue { const char *name; uint64 value; }; static constexpr NamedValue sizes[] = {{"B", 1}, {"KB", 1 << 10}, {"MB", 1 << 20}, {"GB", 1 << 30}}; static constexpr size_t sizes_n = sizeof(sizes) / sizeof(NamedValue); size_t i = 0; while (i + 1 < sizes_n && t.size_ >= 100000 * sizes[i].value) { i++; } logger << t.size_ / sizes[i].value << Slice(sizes[i].name); return logger; } inline Size as_size(uint64 size) { return Size{size}; } /*** Array to string ***/ template struct Array { const ArrayT &ref; }; template StringBuilder &operator<<(StringBuilder &stream, const Array &array) { bool first = true; stream << '{'; for (auto &x : array.ref) { if (!first) { stream << Slice(", "); } stream << x; first = false; } return stream << '}'; } inline StringBuilder &operator<<(StringBuilder &stream, const Array> &array) { bool first = true; stream << '{'; for (bool x : array.ref) { if (!first) { stream << Slice(", "); } stream << x; first = false; } return stream << '}'; } template Array as_array(const ArrayT &array) { return Array{array}; } /*** Tagged ***/ template struct Tagged { Slice tag; const ValueT &ref; }; template StringBuilder &operator<<(StringBuilder &stream, const Tagged &tagged) { return stream << '[' << tagged.tag << ':' << tagged.ref << ']'; } template Tagged tag(Slice tag, const ValueT &ref) { return Tagged{tag, ref}; } /*** Cond ***/ inline StringBuilder &operator<<(StringBuilder &sb, Unit) { return sb; } template struct Cond { bool flag; const TrueT &on_true; const FalseT &on_false; }; template StringBuilder &operator<<(StringBuilder &sb, const Cond &cond) { if (cond.flag) { return sb << cond.on_true; } else { return sb << cond.on_false; } } template Cond cond(bool flag, const TrueT &on_true, const FalseT &on_false = FalseT()) { return Cond{flag, on_true, on_false}; } /*** Concat ***/ template struct Concat { T args; }; template StringBuilder &operator<<(StringBuilder &sb, const Concat &concat) { tuple_for_each(concat.args, [&sb](auto &x) { sb << x; }); return sb; } template auto concat(const ArgsT &...args) { return Concat{std::tie(args...)}; } } // namespace format using format::tag; } // namespace td