/** * Copyright © 2018 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include namespace ipmi { namespace message { namespace details { /************************************** * ipmi return type helpers **************************************/ template void PackBytes(uint8_t* pointer, const NumericType& i) { if constexpr (byteIndex < sizeof(NumericType)) { *pointer = static_cast(i >> (8 * byteIndex)); PackBytes(pointer + 1, i); } } template void PackBytesUnaligned(Payload& p, const NumericType& i) { if constexpr (byteIndex < sizeof(NumericType)) { p.appendBits(CHAR_BIT, static_cast(i >> (8 * byteIndex))); PackBytesUnaligned(p, i); } } /** @struct PackSingle * @brief Utility to pack a single C++ element into a Payload * * User-defined types are expected to specialize this template in order to * get their functionality. * * @tparam S - Type of element to pack. */ template struct PackSingle { /** @brief Do the operation to pack element. * * @param[in] p - Payload to pack into. * @param[out] t - The reference to pack item into. */ static int op(Payload& p, const T& t) { static_assert(std::is_integral_v, "Attempt to pack a type that has no IPMI pack operation"); // if not on a byte boundary, must pack values LSbit/LSByte first if (p.bitCount) { PackBytesUnaligned(p, t); } else { // copy in bits to vector.... p.raw.resize(p.raw.size() + sizeof(T)); uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T); PackBytes(out, t); } return 0; } }; /** @brief Specialization of PackSingle for std::tuple */ template struct PackSingle> { static int op(Payload& p, const std::tuple& v) { return std::apply([&p](const T&... args) { return p.pack(args...); }, v); } }; /** @brief Specialization of PackSingle for std::string * represented as a UCSD-Pascal style string */ template <> struct PackSingle { static int op(Payload& p, const std::string& t) { // check length first uint8_t len; if (t.length() > std::numeric_limits::max()) { using namespace phosphor::logging; log("long string truncated on IPMI message pack"); return 1; } len = static_cast(t.length()); PackSingle::op(p, len); p.append(t.c_str(), t.c_str() + t.length()); return 0; } }; /** @brief Specialization of PackSingle for fixed_uint_t types */ template struct PackSingle> { static int op(Payload& p, const fixed_uint_t& t) { size_t count = N; static_assert(N <= (details::bitStreamSize - CHAR_BIT)); uint64_t bits = t; while (count > 0) { size_t appendCount = std::min(count, static_cast(CHAR_BIT)); p.appendBits(appendCount, static_cast(bits)); bits >>= CHAR_BIT; count -= appendCount; } return 0; } }; /** @brief Specialization of PackSingle for bool. */ template <> struct PackSingle { static int op(Payload& p, const bool& b) { p.appendBits(1, b); return 0; } }; /** @brief Specialization of PackSingle for std::bitset */ template struct PackSingle> { static int op(Payload& p, const std::bitset& t) { size_t count = N; static_assert(N <= (details::bitStreamSize - CHAR_BIT)); unsigned long long bits = t.to_ullong(); while (count > 0) { size_t appendCount = std::min(count, size_t(CHAR_BIT)); p.appendBits(appendCount, static_cast(bits)); bits >>= CHAR_BIT; count -= appendCount; } return 0; } }; /** @brief Specialization of PackSingle for std::optional */ template struct PackSingle> { static int op(Payload& p, const std::optional& t) { int ret = 0; if (t) { ret = PackSingle::op(p, *t); } return ret; } }; /** @brief Specialization of PackSingle for std::array */ template struct PackSingle> { static int op(Payload& p, const std::array& t) { int ret = 0; for (const auto& v : t) { int ret = PackSingle::op(p, v); if (ret) { break; } } return ret; } }; /** @brief Specialization of PackSingle for std::vector */ template struct PackSingle> { static int op(Payload& p, const std::vector& t) { int ret = 0; for (const auto& v : t) { int ret = PackSingle::op(p, v); if (ret) { break; } } return ret; } }; /** @brief Specialization of PackSingle for std::vector */ template <> struct PackSingle> { static int op(Payload& p, const std::vector& t) { p.raw.reserve(p.raw.size() + t.size()); p.raw.insert(p.raw.end(), t.begin(), t.end()); return 0; } }; /** @brief Specialization of PackSingle for std::variant */ template struct PackSingle> { static int op(Payload& p, const std::variant& v) { return std::visit( [&p](const auto& arg) { return PackSingle>::op(p, arg); }, v); } }; } // namespace details } // namespace message } // namespace ipmi