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 103 { 104 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 0; 121 } 122 } 123 } 124 }; 125 126 /** @struct UnpackSingle 127 * @brief Utility to unpack a single C++ element from a Payload 128 * 129 * Specialization to unpack std::string represented as a 130 * UCSD-Pascal style string 131 */ 132 template <> 133 struct UnpackSingle<std::string> 134 { 135 static int op(Payload& p, std::string& t) 136 { 137 // pop len first 138 if (p.rawIndex > (p.raw.size() - sizeof(uint8_t))) 139 { 140 return 1; 141 } 142 uint8_t len = p.raw[p.rawIndex++]; 143 // check to see that there are n bytes left 144 auto [first, last] = p.pop<char>(len); 145 if (first == last) 146 { 147 return 1; 148 } 149 t.reserve(last - first); 150 t.insert(0, first, (last - first)); 151 return 0; 152 } 153 }; 154 155 /** @brief Specialization of UnpackSingle for fixed_uint_t types 156 */ 157 template <unsigned N> 158 struct UnpackSingle<fixed_uint_t<N>> 159 { 160 static int op(Payload& p, fixed_uint_t<N>& t) 161 { 162 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 163 constexpr size_t count = N; 164 // acquire enough bits in the stream to fulfill the Payload 165 if (p.fillBits(count)) 166 { 167 return -1; 168 } 169 fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1); 170 t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>(); 171 p.bitStream >>= count; 172 p.bitCount -= count; 173 return 0; 174 } 175 }; 176 177 /** @brief Specialization of UnpackSingle for bool. */ 178 template <> 179 struct UnpackSingle<bool> 180 { 181 static int op(Payload& p, bool& b) 182 { 183 // acquire enough bits in the stream to fulfill the Payload 184 if (p.fillBits(1)) 185 { 186 return -1; 187 } 188 b = static_cast<bool>(p.bitStream & 0x01); 189 // clear bits from stream 190 p.bitStream >>= 1; 191 p.bitCount -= 1; 192 return 0; 193 } 194 }; 195 196 /** @brief Specialization of UnpackSingle for std::bitset<N> 197 */ 198 template <size_t N> 199 struct UnpackSingle<std::bitset<N>> 200 { 201 static int op(Payload& p, std::bitset<N>& t) 202 { 203 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 204 size_t count = N; 205 // acquire enough bits in the stream to fulfill the Payload 206 if (p.fillBits(count)) 207 { 208 return -1; 209 } 210 fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1); 211 t |= (p.bitStream & bitmask).convert_to<unsigned long long>(); 212 p.bitStream >>= count; 213 p.bitCount -= count; 214 return 0; 215 } 216 }; 217 218 /** @brief Specialization of UnpackSingle for std::optional<T> */ 219 template <typename T> 220 struct UnpackSingle<std::optional<T>> 221 { 222 static int op(Payload& p, std::optional<T>& t) 223 { 224 bool priorError = p.unpackError; 225 size_t priorIndex = p.rawIndex; 226 // more stuff to unroll if partial bytes are out 227 size_t priorBitCount = p.bitCount; 228 fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream; 229 t.emplace(); 230 int ret = UnpackSingle<T>::op(p, *t); 231 if (ret != 0) 232 { 233 t.reset(); 234 p.rawIndex = priorIndex; 235 p.bitStream = priorBits; 236 p.bitCount = priorBitCount; 237 p.unpackError = priorError; 238 } 239 return 0; 240 } 241 }; 242 243 /** @brief Specialization of UnpackSingle for std::array<T, N> */ 244 template <typename T, size_t N> 245 struct UnpackSingle<std::array<T, N>> 246 { 247 static int op(Payload& p, std::array<T, N>& t) 248 { 249 int ret = 0; 250 size_t priorIndex = p.rawIndex; 251 for (auto& v : t) 252 { 253 ret = UnpackSingle<T>::op(p, v); 254 if (ret) 255 { 256 p.rawIndex = priorIndex; 257 t = std::array<T, N>(); 258 break; 259 } 260 } 261 return ret; 262 } 263 }; 264 265 /** @brief Specialization of UnpackSingle for std::array<uint8_t> */ 266 template <size_t N> 267 struct UnpackSingle<std::array<uint8_t, N>> 268 { 269 static int op(Payload& p, std::array<uint8_t, N>& t) 270 { 271 if (p.raw.size() - p.rawIndex < N) 272 { 273 t.fill(0); 274 return -1; 275 } 276 // copy out the bytes 277 std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N, 278 t.begin()); 279 p.rawIndex += N; 280 return 0; 281 } 282 }; 283 284 /** @brief Specialization of UnpackSingle for std::vector<T> */ 285 template <typename T> 286 struct UnpackSingle<std::vector<T>> 287 { 288 static int op(Payload& p, std::vector<T>& t) 289 { 290 int ret = 0; 291 while (p.rawIndex < p.raw.size()) 292 { 293 t.emplace_back(); 294 ret = UnpackSingle<T>::op(p, t.back()); 295 if (ret) 296 { 297 t.pop_back(); 298 break; 299 } 300 } 301 return ret; 302 } 303 }; 304 305 /** @brief Specialization of UnpackSingle for std::vector<uint8_t> */ 306 template <> 307 struct UnpackSingle<std::vector<uint8_t>> 308 { 309 static int op(Payload& p, std::vector<uint8_t>& t) 310 { 311 // copy out the remainder of the message 312 t.reserve(p.raw.size() - p.rawIndex); 313 t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end()); 314 p.rawIndex = p.raw.size(); 315 return 0; 316 } 317 }; 318 319 /** @brief Specialization of UnpackSingle for Payload */ 320 template <> 321 struct UnpackSingle<Payload> 322 { 323 static int op(Payload& p, Payload& t) 324 { 325 // mark that this payload is being included in the args 326 p.trailingOk = true; 327 t = p; 328 // reset the unpacking flags so it can be properly checked 329 t.trailingOk = false; 330 t.unpackCheck = true; 331 t.unpackError = false; 332 return 0; 333 } 334 }; 335 336 } // namespace details 337 338 } // namespace message 339 340 } // namespace ipmi 341