#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace logging::mipi { namespace detail { template constexpr auto to_message() { constexpr auto s = S::value; using char_t = typename std::remove_cv_t::value_type; return [&](std::integer_sequence) { return sc::message< static_cast(L), sc::undefined, char_t, s[Is]...>>{}; }(std::make_integer_sequence{}); } template constexpr auto to_module() { constexpr auto s = std::string_view{S}; return [&](std::integer_sequence) { return sc::module_string>{}; }(std::make_integer_sequence{}); } } // namespace detail namespace defn { using msg::at; using msg::dword_index_t; using msg::field; using msg::message; using msg::operator""_msb; using msg::operator""_lsb; enum struct type : uint8_t { Build = 0, Short32 = 1, Catalog = 3 }; enum struct build_subtype : uint8_t { Compact32 = 0, Compact64 = 1, Long = 2 }; enum struct catalog_subtype : uint8_t { Id32_Pack32 = 1 }; using type_f = field<"type", type>::located; using opt_len_f = field<"opt_len", bool>::located; using payload_len_f = field<"payload_len", std::uint16_t>::located; using build_subtype_f = field<"subtype", build_subtype>::located; using compact32_build_id_f = field<"build_id", std::uint32_t>::located< at{dword_index_t{0}, 31_msb, 30_lsb}, at{dword_index_t{0}, 23_msb, 4_lsb}>; using compact64_build_id_f = field<"build_id", std::uint64_t>::located< at{dword_index_t{1}, 31_msb, 0_lsb}, at{dword_index_t{0}, 31_msb, 30_lsb}, at{dword_index_t{0}, 23_msb, 4_lsb}>; using normal_build_msg_t = message<"normal_build", type_f::with_required, opt_len_f::with_required, build_subtype_f::with_required, payload_len_f>; using compact32_build_msg_t = message<"compact32_build", type_f::with_required, build_subtype_f::with_required, compact32_build_id_f>; using compact64_build_msg_t = message<"compact64_build", type_f::with_required, build_subtype_f::with_required, compact64_build_id_f>; using short32_payload_f = field<"payload", std::uint32_t>::located; using short32_msg_t = message<"short32", type_f::with_required, short32_payload_f>; using catalog_subtype_f = field<"subtype", catalog_subtype>::located; using severity_f = field<"severity", std::uint8_t>::located; using module_id_f = field<"module_id", std::uint8_t>::located; using catalog_msg_t = message<"catalog", type_f::with_required, severity_f, module_id_f, catalog_subtype_f::with_required>; } // namespace defn template struct log_handler { constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {} template ALWAYS_INLINE auto log(FilenameStringType, LineNumberType, MsgType const &msg) -> void { log_msg(msg); } template ALWAYS_INLINE auto log_msg(Msg msg) -> void { msg.apply([&](S, Args... args) { constexpr auto L = stdx::to_underlying(get_level(Env{}).value); using Message = decltype(detail::to_message()); using Module = decltype(detail::to_module()); dispatch_message(catalog(), module(), static_cast(args)...); }); } template auto log_build() -> void { using namespace msg; if constexpr (S.empty() and stdx::bit_width(Version) <= 22) { owning message{"build_id"_field = Version}; dispatch_pass_by_args(message.data()[0]); } else if constexpr (S.empty() and stdx::bit_width(Version) <= 54) { owning message{"build_id"_field = Version}; dispatch_pass_by_args(message.data()[0], message.data()[1]); } else { constexpr auto header_size = defn::normal_build_msg_t::size::value; constexpr auto payload_len = S.size() + sizeof(std::uint64_t); using storage_t = std::array; defn::normal_build_msg_t::owner_t message{ "payload_len"_field = payload_len}; auto dest = &message.data()[header_size]; auto const ver = stdx::to_le(static_cast(Version)); dest = std::copy_n(stdx::bit_cast(&ver), sizeof(std::uint64_t), dest); std::copy_n(std::cbegin(S.value), S.size(), dest); dispatch_pass_by_buffer(message.data()); } } private: template NEVER_INLINE auto dispatch_pass_by_args(MsgDataTypes &&...msg_data) -> void { stdx::for_each( [&](Dest &dest) { conc::call_in_critical_section([&] { dest.log_by_args(std::forward(msg_data)...); }); }, dests); } NEVER_INLINE auto dispatch_pass_by_buffer(stdx::span msg) -> void { stdx::for_each( [&](Dest &dest) { conc::call_in_critical_section( [&] { dest.log_by_buf(msg); }); }, dests); } template ... MsgDataTypes> ALWAYS_INLINE auto dispatch_message(string_id id, [[maybe_unused]] module_id m, MsgDataTypes... msg_data) -> void { using namespace msg; if constexpr (sizeof...(msg_data) == 0u) { owning message{"payload"_field = id}; dispatch_pass_by_args(message.data()[0]); } else if constexpr (sizeof...(MsgDataTypes) <= 2u) { owning message{"severity"_field = Level, "module_id"_field = m}; dispatch_pass_by_args( message.data()[0], stdx::to_le(id), stdx::to_le(std::forward(msg_data))...); } else { constexpr auto header_size = defn::catalog_msg_t::size::value; constexpr auto payload_len = (sizeof(id) + ... + sizeof(MsgDataTypes)); using storage_t = std::array; defn::catalog_msg_t::owner_t message{ "severity"_field = Level, "module_id"_field = m}; constexpr auto copy_arg = [](std::uint32_t arg, auto &dest) { std::memcpy(dest, &arg, sizeof(std::uint32_t)); dest += sizeof(std::uint32_t); }; auto dest = &message.data()[header_size]; copy_arg(stdx::to_le(id), dest); (copy_arg(stdx::to_le(msg_data), dest), ...); dispatch_pass_by_buffer(message.data()); } } TDestinations dests; }; template struct config { using destinations_tuple_t = stdx::tuple; constexpr explicit config(TDestinations... dests) : logger{stdx::tuple{std::move(dests)...}} {} log_handler logger; }; template config(Ts...) -> config; } // namespace logging::mipi