1 /** 2 * Copyright © 2018 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include <array> 19 #include <ipmid/message/types.hpp> 20 #include <memory> 21 #include <optional> 22 #include <phosphor-logging/log.hpp> 23 #include <string_view> 24 #include <tuple> 25 #include <utility> 26 #include <variant> 27 #include <vector> 28 29 namespace ipmi 30 { 31 32 namespace message 33 { 34 35 namespace details 36 { 37 38 /************************************** 39 * ipmi return type helpers 40 **************************************/ 41 42 template <typename NumericType, size_t byteIndex = 0> 43 void PackBytes(uint8_t* pointer, const NumericType& i) 44 { 45 if constexpr (byteIndex < sizeof(NumericType)) 46 { 47 *pointer = static_cast<uint8_t>(i >> (8 * byteIndex)); 48 PackBytes<NumericType, byteIndex + 1>(pointer + 1, i); 49 } 50 } 51 52 template <typename NumericType, size_t byteIndex = 0> 53 void PackBytesUnaligned(Payload& p, const NumericType& i) 54 { 55 if constexpr (byteIndex < sizeof(NumericType)) 56 { 57 p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex))); 58 PackBytesUnaligned<NumericType, byteIndex + 1>(p, i); 59 } 60 } 61 62 /** @struct PackSingle 63 * @brief Utility to pack a single C++ element into a Payload 64 * 65 * User-defined types are expected to specialize this template in order to 66 * get their functionality. 67 * 68 * @tparam S - Type of element to pack. 69 */ 70 template <typename T> 71 struct PackSingle 72 { 73 /** @brief Do the operation to pack element. 74 * 75 * @param[in] p - Payload to pack into. 76 * @param[out] t - The reference to pack item into. 77 */ 78 static int op(Payload& p, const T& t) 79 { 80 static_assert(std::is_integral_v<T>, 81 "Attempt to pack a type that has no IPMI pack operation"); 82 // if not on a byte boundary, must pack values LSbit/LSByte first 83 if (p.bitCount) 84 { 85 PackBytesUnaligned<T>(p, t); 86 } 87 else 88 { 89 // copy in bits to vector.... 90 p.raw.resize(p.raw.size() + sizeof(T)); 91 uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T); 92 PackBytes<T>(out, t); 93 } 94 return 0; 95 } 96 }; 97 98 /** @brief Specialization of PackSingle for std::tuple<T> */ 99 template <typename... T> 100 struct PackSingle<std::tuple<T...>> 101 { 102 static int op(Payload& p, const std::tuple<T...>& v) 103 { 104 return std::apply([&p](const T&... args) { return p.pack(args...); }, 105 v); 106 } 107 }; 108 109 /** @brief Specialization of PackSingle for std::string 110 * represented as a UCSD-Pascal style string 111 */ 112 template <> 113 struct PackSingle<std::string> 114 { 115 static int op(Payload& p, const std::string& t) 116 { 117 // check length first 118 uint8_t len; 119 if (t.length() > std::numeric_limits<decltype(len)>::max()) 120 { 121 using namespace phosphor::logging; 122 log<level::ERR>("long string truncated on IPMI message pack"); 123 return 1; 124 } 125 len = static_cast<uint8_t>(t.length()); 126 PackSingle<uint8_t>::op(p, len); 127 p.append(t.c_str(), t.c_str() + t.length()); 128 return 0; 129 } 130 }; 131 132 /** @brief Specialization of PackSingle for fixed_uint_t types 133 */ 134 template <unsigned N> 135 struct PackSingle<fixed_uint_t<N>> 136 { 137 static int op(Payload& p, const fixed_uint_t<N>& t) 138 { 139 size_t count = N; 140 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 141 uint64_t bits = t; 142 while (count > 0) 143 { 144 size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT)); 145 p.appendBits(appendCount, static_cast<uint8_t>(bits)); 146 bits >>= CHAR_BIT; 147 count -= appendCount; 148 } 149 return 0; 150 } 151 }; 152 153 /** @brief Specialization of PackSingle for bool. */ 154 template <> 155 struct PackSingle<bool> 156 { 157 static int op(Payload& p, const bool& b) 158 { 159 p.appendBits(1, b); 160 return 0; 161 } 162 }; 163 164 /** @brief Specialization of PackSingle for std::bitset<N> */ 165 template <size_t N> 166 struct PackSingle<std::bitset<N>> 167 { 168 static int op(Payload& p, const std::bitset<N>& t) 169 { 170 size_t count = N; 171 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 172 unsigned long long bits = t.to_ullong(); 173 while (count > 0) 174 { 175 size_t appendCount = std::min(count, size_t(CHAR_BIT)); 176 p.appendBits(appendCount, static_cast<uint8_t>(bits)); 177 bits >>= CHAR_BIT; 178 count -= appendCount; 179 } 180 return 0; 181 } 182 }; 183 184 /** @brief Specialization of PackSingle for std::optional<T> */ 185 template <typename T> 186 struct PackSingle<std::optional<T>> 187 { 188 static int op(Payload& p, const std::optional<T>& t) 189 { 190 int ret = 0; 191 if (t) 192 { 193 ret = PackSingle<T>::op(p, *t); 194 } 195 return ret; 196 } 197 }; 198 199 /** @brief Specialization of PackSingle for std::array<T, N> */ 200 template <typename T, size_t N> 201 struct PackSingle<std::array<T, N>> 202 { 203 static int op(Payload& p, const std::array<T, N>& t) 204 { 205 int ret = 0; 206 for (const auto& v : t) 207 { 208 int ret = PackSingle<T>::op(p, v); 209 if (ret) 210 { 211 break; 212 } 213 } 214 return ret; 215 } 216 }; 217 218 /** @brief Specialization of PackSingle for std::vector<T> */ 219 template <typename T> 220 struct PackSingle<std::vector<T>> 221 { 222 static int op(Payload& p, const std::vector<T>& t) 223 { 224 int ret = 0; 225 for (const auto& v : t) 226 { 227 int ret = PackSingle<T>::op(p, v); 228 if (ret) 229 { 230 break; 231 } 232 } 233 return ret; 234 } 235 }; 236 237 /** @brief Specialization of PackSingle for std::vector<uint8_t> */ 238 template <> 239 struct PackSingle<std::vector<uint8_t>> 240 { 241 static int op(Payload& p, const std::vector<uint8_t>& t) 242 { 243 if (p.bitCount != 0) 244 { 245 return 1; 246 } 247 p.raw.reserve(p.raw.size() + t.size()); 248 p.raw.insert(p.raw.end(), t.begin(), t.end()); 249 return 0; 250 } 251 }; 252 253 /** @brief Specialization of PackSingle for std::string_view */ 254 template <> 255 struct PackSingle<std::string_view> 256 { 257 static int op(Payload& p, const std::string_view& t) 258 { 259 if (p.bitCount != 0) 260 { 261 return 1; 262 } 263 p.raw.reserve(p.raw.size() + t.size()); 264 p.raw.insert(p.raw.end(), t.begin(), t.end()); 265 return 0; 266 } 267 }; 268 269 /** @brief Specialization of PackSingle for std::variant<T, N> */ 270 template <typename... T> 271 struct PackSingle<std::variant<T...>> 272 { 273 static int op(Payload& p, const std::variant<T...>& v) 274 { 275 return std::visit( 276 [&p](const auto& arg) { 277 return PackSingle<std::decay_t<decltype(arg)>>::op(p, arg); 278 }, 279 v); 280 } 281 }; 282 283 /** @brief Specialization of PackSingle for Payload */ 284 template <> 285 struct PackSingle<Payload> 286 { 287 static int op(Payload& p, const Payload& t) 288 { 289 if (p.bitCount != 0 || t.bitCount != 0) 290 { 291 return 1; 292 } 293 p.raw.reserve(p.raw.size() + t.raw.size()); 294 p.raw.insert(p.raw.end(), t.raw.begin(), t.raw.end()); 295 return 0; 296 } 297 }; 298 299 } // namespace details 300 301 } // namespace message 302 303 } // namespace ipmi 304