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