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