xref: /openbmc/phosphor-bmc-code-mgmt/common/include/utils.hpp (revision dcf4b607bd2937a8964d68fe9dcc59daaf20c720)
1 #pragma once
2 
3 #include <sdbusplus/async.hpp>
4 
5 #include <functional>
6 #include <optional>
7 
8 /**
9  * @brief Asynchronously executes a shell command.
10  * @param ctx Async context for monitoring the pipe.
11  * @param cmd Shell command to execute.
12  * @return Task resolving to true on success (exit code 0), false otherwise.
13  */
14 sdbusplus::async::task<bool> asyncSystem(
15     sdbusplus::async::context& ctx, const std::string& cmd,
16     std::optional<std::reference_wrapper<std::string>> result = std::nullopt);
17 
18 /**
19  * @brief Convert bytes to an integer of the given type.
20  *
21  * @tparam IntegerType Output integer type (e.g., uint16_t, uint32_t).
22  * @tparam Container A container of uint8_t bytes.
23  * @param data Byte data to convert.
24  * @param bigEndian Set true for big-endian order; false for little-endian.
25  * @return Converted integer.
26  */
27 template <typename IntegerType, typename Container>
28 IntegerType bytesToInt(const Container& data, bool bigEndian = false)
29 {
30     static_assert(std::is_integral_v<IntegerType>,
31                   "IntegerType must be an integral type");
32     static_assert(std::is_same_v<typename Container::value_type, uint8_t>,
33                   "Container must hold uint8_t elements");
34 
35     constexpr size_t maxBytes = sizeof(IntegerType);
36     size_t size = std::min(data.size(), maxBytes);
37 
38     IntegerType result = 0;
39     for (size_t i = 0; i < size; ++i)
40     {
41         size_t shift = bigEndian ? (size - 1 - i) * 8 : i * 8;
42         result |= static_cast<IntegerType>(data[i]) << shift;
43     }
44 
45     return result;
46 }
47 
48 template <typename>
49 inline constexpr bool always_false = false;
50 
51 /**
52  * @brief Constructs a vector of bytes (`std::vector<uint8_t>`) from a variable
53  *        number of arguments, which can include enums, integral values,
54  *        and initializer lists.
55  *
56  * This function is useful when building byte packets or command sequences
57  * to be sent over communication protocols (e.g., I2C, UART, SPI).
58  *
59  * @tparam Args Types of arguments to convert into bytes
60  * @param args The values to encode into the byte vector
61  * @return std::vector<uint8_t> A flattened list of bytes
62  *
63  * @note Passing unsupported types will trigger a compile-time static_assert.
64  * @note Endianness: Multi-byte integers use little-endian order.
65  *
66  * @code
67  * enum class Command : uint8_t { Start = 0x01 };
68  * auto buf = buildByteVector(Command::Start, 0x1234, {0xAA, 0xBB});
69  * // Result: { 0x01, 0x34, 0x12, 0xAA, 0xBB }
70  * @endcode
71  */
72 template <typename... Args>
buildByteVector(Args &&...args)73 std::vector<uint8_t> buildByteVector(Args&&... args)
74 {
75     std::vector<uint8_t> buf;
76 
77     auto append = [&](auto&& value) {
78         using T = std::decay_t<decltype(value)>;
79 
80         if constexpr (std::is_enum_v<T>)
81         {
82             buf.push_back(static_cast<uint8_t>(value));
83         }
84         else if constexpr (std::is_integral_v<T>)
85         {
86             for (size_t i = 0; i < sizeof(T); ++i)
87             {
88                 buf.push_back(static_cast<uint8_t>(value >> (i * 8)));
89             }
90         }
91         else if constexpr (std::is_same_v<T, std::initializer_list<uint8_t>>)
92         {
93             buf.insert(buf.end(), value.begin(), value.end());
94         }
95         else
96         {
97             static_assert(always_false<T>,
98                           "Unsupported type in buildByteVector");
99         }
100     };
101 
102     (append(std::forward<Args>(args)), ...);
103 
104     return buf;
105 }
106