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 <ipmid/message/types.hpp> 19 20 #include <array> 21 #include <optional> 22 #include <span> 23 #include <string> 24 #include <tuple> 25 #include <vector> 26 27 namespace ipmi 28 { 29 30 namespace message 31 { 32 33 namespace details 34 { 35 36 /************************************** 37 * ipmi return type helpers 38 **************************************/ 39 40 template <typename NumericType, size_t byteIndex = 0> 41 void UnpackBytes(uint8_t* pointer, NumericType& i) 42 { 43 if constexpr (byteIndex < sizeof(NumericType)) 44 { 45 i |= static_cast<NumericType>(*pointer) << (CHAR_BIT * byteIndex); 46 UnpackBytes<NumericType, byteIndex + 1>(pointer + 1, i); 47 } 48 } 49 50 template <typename NumericType, size_t byteIndex = 0> 51 void UnpackBytesUnaligned(Payload& p, NumericType& i) 52 { 53 if constexpr (byteIndex < sizeof(NumericType)) 54 { 55 i |= static_cast<NumericType>(p.popBits(CHAR_BIT)) 56 << (CHAR_BIT * byteIndex); 57 UnpackBytesUnaligned<NumericType, byteIndex + 1>(p, i); 58 } 59 } 60 61 /** @struct UnpackSingle 62 * @brief Utility to unpack a single C++ element from a Payload 63 * 64 * User-defined types are expected to specialize this template in order to 65 * get their functionality. 66 * 67 * @tparam T - Type of element to unpack. 68 */ 69 template <typename T> 70 struct UnpackSingle 71 { 72 /** @brief Do the operation to unpack element. 73 * 74 * @param[in] p - Payload to unpack from. 75 * @param[out] t - The reference to unpack item into. 76 */ 77 static int op(Payload& p, T& t) 78 { 79 if constexpr (std::is_fundamental<T>::value) 80 { 81 t = 0; 82 if (p.bitCount) 83 { 84 if (p.fillBits(CHAR_BIT * sizeof(t))) 85 { 86 return 1; 87 } 88 UnpackBytesUnaligned<T>(p, t); 89 } 90 else 91 { 92 // copy out bits from vector.... 93 if (p.raw.size() < (p.rawIndex + sizeof(t))) 94 { 95 return 1; 96 } 97 auto iter = p.raw.data() + p.rawIndex; 98 t = 0; 99 UnpackBytes<T>(iter, t); 100 p.rawIndex += sizeof(t); 101 } 102 return 0; 103 } 104 else if constexpr (utility::is_tuple<T>::value) 105 { 106 bool priorError = p.unpackError; 107 size_t priorIndex = p.rawIndex; 108 // more stuff to unroll if partial bytes are out 109 size_t priorBitCount = p.bitCount; 110 fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream; 111 int ret = p.unpack(t); 112 if (ret != 0) 113 { 114 t = T(); 115 p.rawIndex = priorIndex; 116 p.bitStream = priorBits; 117 p.bitCount = priorBitCount; 118 p.unpackError = priorError; 119 } 120 return ret; 121 } 122 else 123 { 124 static_assert( 125 utility::dependent_false<T>::value, 126 "Attempt to unpack a type that has no IPMI unpack operation"); 127 } 128 } 129 }; 130 131 /** @struct UnpackSingle 132 * @brief Utility to unpack a single C++ element from a Payload 133 * 134 * Specialization to unpack std::string represented as a 135 * UCSD-Pascal style string 136 */ 137 template <> 138 struct UnpackSingle<std::string> 139 { 140 static int op(Payload& p, std::string& t) 141 { 142 // pop len first 143 if (p.rawIndex > (p.raw.size() - sizeof(uint8_t))) 144 { 145 return 1; 146 } 147 uint8_t len = p.raw[p.rawIndex++]; 148 // check to see that there are n bytes left 149 auto [first, last] = p.pop<char>(len); 150 if (first == last) 151 { 152 return 1; 153 } 154 t.reserve(last - first); 155 t.insert(0, first, (last - first)); 156 return 0; 157 } 158 }; 159 160 /** @brief Specialization of UnpackSingle for fixed_uint_t types 161 */ 162 template <bitcount_t N> 163 struct UnpackSingle<fixed_uint_t<N>> 164 { 165 static int op(Payload& p, fixed_uint_t<N>& t) 166 { 167 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 168 constexpr size_t count = N; 169 // acquire enough bits in the stream to fulfill the Payload 170 if (p.fillBits(count)) 171 { 172 return -1; 173 } 174 fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1); 175 t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>(); 176 p.bitStream >>= count; 177 p.bitCount -= count; 178 return 0; 179 } 180 }; 181 182 /** @brief Specialization of UnpackSingle for bool. */ 183 template <> 184 struct UnpackSingle<bool> 185 { 186 static int op(Payload& p, bool& b) 187 { 188 // acquire enough bits in the stream to fulfill the Payload 189 if (p.fillBits(1)) 190 { 191 return -1; 192 } 193 b = static_cast<bool>(p.bitStream & 0x01); 194 // clear bits from stream 195 p.bitStream >>= 1; 196 p.bitCount -= 1; 197 return 0; 198 } 199 }; 200 201 /** @brief Specialization of UnpackSingle for std::bitset<N> 202 */ 203 template <size_t N> 204 struct UnpackSingle<std::bitset<N>> 205 { 206 static int op(Payload& p, std::bitset<N>& t) 207 { 208 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 209 size_t count = N; 210 // acquire enough bits in the stream to fulfill the Payload 211 if (p.fillBits(count)) 212 { 213 return -1; 214 } 215 fixed_uint_t<details::bitStreamSize> bitmask = 216 ~fixed_uint_t<details::bitStreamSize>(0) >> 217 (details::bitStreamSize - count); 218 t |= (p.bitStream & bitmask).convert_to<unsigned long long>(); 219 p.bitStream >>= count; 220 p.bitCount -= count; 221 return 0; 222 } 223 }; 224 225 /** @brief Specialization of UnpackSingle for std::optional<T> */ 226 template <typename T> 227 struct UnpackSingle<std::optional<T>> 228 { 229 static int op(Payload& p, std::optional<T>& t) 230 { 231 bool priorError = p.unpackError; 232 size_t priorIndex = p.rawIndex; 233 // more stuff to unroll if partial bytes are out 234 size_t priorBitCount = p.bitCount; 235 fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream; 236 T value; 237 int ret = UnpackSingle<T>::op(p, value); 238 if (ret != 0) 239 { 240 t.reset(); 241 p.rawIndex = priorIndex; 242 p.bitStream = priorBits; 243 p.bitCount = priorBitCount; 244 p.unpackError = priorError; 245 } 246 else 247 { 248 t.emplace(std::move(value)); 249 } 250 return 0; 251 } 252 }; 253 254 /** @brief Specialization of UnpackSingle for std::array<T, N> */ 255 template <typename T, size_t N> 256 struct UnpackSingle<std::array<T, N>> 257 { 258 static int op(Payload& p, std::array<T, N>& t) 259 { 260 int ret = 0; 261 size_t priorIndex = p.rawIndex; 262 for (auto& v : t) 263 { 264 ret = UnpackSingle<T>::op(p, v); 265 if (ret) 266 { 267 p.rawIndex = priorIndex; 268 t = std::array<T, N>(); 269 break; 270 } 271 } 272 return ret; 273 } 274 }; 275 276 /** @brief Specialization of UnpackSingle for std::array<uint8_t> */ 277 template <size_t N> 278 struct UnpackSingle<std::array<uint8_t, N>> 279 { 280 static int op(Payload& p, std::array<uint8_t, N>& t) 281 { 282 if (p.raw.size() - p.rawIndex < N) 283 { 284 t.fill(0); 285 return -1; 286 } 287 // copy out the bytes 288 std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N, 289 t.begin()); 290 p.rawIndex += N; 291 return 0; 292 } 293 }; 294 295 /** @brief Specialization of UnpackSingle for std::vector<T> */ 296 template <typename T> 297 struct UnpackSingle<std::vector<T>> 298 { 299 static int op(Payload& p, std::vector<T>& t) 300 { 301 while (p.rawIndex < p.raw.size()) 302 { 303 t.emplace_back(); 304 if (UnpackSingle<T>::op(p, t.back())) 305 { 306 t.pop_back(); 307 break; 308 } 309 } 310 // unpacking a vector is always successful: 311 // either stuff was unpacked successfully (return 0) 312 // or stuff was not unpacked, but should still return 313 // success because an empty vector or a not-fully-unpacked 314 // payload is not a failure. 315 return 0; 316 } 317 }; 318 319 /** @brief Specialization of UnpackSingle for std::vector<uint8_t> */ 320 template <> 321 struct UnpackSingle<std::vector<uint8_t>> 322 { 323 static int op(Payload& p, std::vector<uint8_t>& t) 324 { 325 // copy out the remainder of the message 326 t.reserve(p.raw.size() - p.rawIndex); 327 t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end()); 328 p.rawIndex = p.raw.size(); 329 return 0; 330 } 331 }; 332 333 /** @brief Specialization of UnpackSingle for SecureBuffer */ 334 template <> 335 struct UnpackSingle<SecureBuffer> 336 { 337 static int op(Payload& p, SecureBuffer& t) 338 { 339 // copy out the remainder of the message 340 t.reserve(p.raw.size() - p.rawIndex); 341 t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end()); 342 p.rawIndex = p.raw.size(); 343 return 0; 344 } 345 }; 346 347 /** @brief Specialization of UnpackSingle for std::span<const uint8_t> */ 348 template <> 349 struct UnpackSingle<std::span<const uint8_t>> 350 { 351 static int op(Payload& p, std::span<const uint8_t>& t) 352 { 353 // copy out the remainder of the message 354 t = std::span<const uint8_t>(p.raw.begin() + p.rawIndex, p.raw.end()); 355 p.rawIndex = p.raw.size(); 356 return 0; 357 } 358 }; 359 360 /** @brief Specialization of UnpackSingle for Payload */ 361 template <> 362 struct UnpackSingle<Payload> 363 { 364 static int op(Payload& p, Payload& t) 365 { 366 t = p; 367 // mark that this payload is being included in the args 368 p.trailingOk = true; 369 return 0; 370 } 371 }; 372 373 } // namespace details 374 375 } // namespace message 376 377 } // namespace ipmi 378