diff options
author | Vernon Mauery <vernon.mauery@linux.intel.com> | 2018-10-08 12:05:16 -0700 |
---|---|---|
committer | Vernon Mauery <vernon.mauery@linux.intel.com> | 2019-02-25 14:27:21 -0800 |
commit | e7329c71f3d22e010c38a7f738e81ab78330038e (patch) | |
tree | 8cb355cbde96d7a341f1c707c7f0e8c334f88e58 /include/ipmid/message/unpack.hpp | |
parent | 1bb0c7fc55b21bb40b7afb567f2f938f15411ca1 (diff) | |
download | phosphor-host-ipmid-e7329c71f3d22e010c38a7f738e81ab78330038e.tar.gz phosphor-host-ipmid-e7329c71f3d22e010c38a7f738e81ab78330038e.zip |
ipmid: Compiler-generated unpacking and packing of messages
handler.hpp has the templated wrapping bits for ipmi command handler
callbacks implemented.
message.hpp has the serialization/deserialization of the ipmi data
stream into packed tuples for functions.
message/pack.hpp and message/unpack.hpp contain the actual serialization
and deserialization of types.
Change-Id: If997f8768c8488ab6ac022526a5ef9a1bce57fcb
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Diffstat (limited to 'include/ipmid/message/unpack.hpp')
-rw-r--r-- | include/ipmid/message/unpack.hpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/include/ipmid/message/unpack.hpp b/include/ipmid/message/unpack.hpp new file mode 100644 index 0000000..d96928f --- /dev/null +++ b/include/ipmid/message/unpack.hpp @@ -0,0 +1,340 @@ +/** + * Copyright © 2018 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <array> +#include <ipmid/message/types.hpp> +#include <optional> +#include <string> +#include <tuple> +#include <vector> + +namespace ipmi +{ + +namespace message +{ + +namespace details +{ + +/************************************** + * ipmi return type helpers + **************************************/ + +template <typename NumericType, size_t byteIndex = 0> +void UnpackBytes(uint8_t* pointer, NumericType& i) +{ + if constexpr (byteIndex < sizeof(NumericType)) + { + i |= static_cast<NumericType>(*pointer) << (CHAR_BIT * byteIndex); + UnpackBytes<NumericType, byteIndex + 1>(pointer + 1, i); + } +} + +template <typename NumericType, size_t byteIndex = 0> +void UnpackBytesUnaligned(Payload& p, NumericType& i) +{ + if constexpr (byteIndex < sizeof(NumericType)) + { + i |= static_cast<NumericType>(p.popBits(CHAR_BIT)) + << (CHAR_BIT * byteIndex); + UnpackBytesUnaligned<NumericType, byteIndex + 1>(p, i); + } +} + +/** @struct UnpackSingle + * @brief Utility to unpack a single C++ element from a Payload + * + * User-defined types are expected to specialize this template in order to + * get their functionality. + * + * @tparam T - Type of element to unpack. + */ +template <typename T> +struct UnpackSingle +{ + /** @brief Do the operation to unpack element. + * + * @param[in] p - Payload to unpack from. + * @param[out] t - The reference to unpack item into. + */ + static int op(Payload& p, T& t) + { + if constexpr (std::is_fundamental<T>::value) + { + t = 0; + if (p.bitCount) + { + if (p.fillBits(CHAR_BIT * sizeof(t))) + { + return 1; + } + UnpackBytesUnaligned<T>(p, t); + } + else + { + // copy out bits from vector.... + if (p.raw.size() < (p.rawIndex + sizeof(t))) + { + return 1; + } + auto iter = p.raw.data() + p.rawIndex; + t = 0; + UnpackBytes<T>(iter, t); + p.rawIndex += sizeof(t); + } + return 0; + } + else + { + if constexpr (utility::is_tuple<T>::value) + { + bool priorError = p.unpackError; + size_t priorIndex = p.rawIndex; + // more stuff to unroll if partial bytes are out + size_t priorBitCount = p.bitCount; + fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream; + int ret = p.unpack(t); + if (ret != 0) + { + t = T(); + p.rawIndex = priorIndex; + p.bitStream = priorBits; + p.bitCount = priorBitCount; + p.unpackError = priorError; + } + return 0; + } + } + } +}; + +/** @struct UnpackSingle + * @brief Utility to unpack a single C++ element from a Payload + * + * Specialization to unpack std::string represented as a + * UCSD-Pascal style string + */ +template <> +struct UnpackSingle<std::string> +{ + static int op(Payload& p, std::string& t) + { + // pop len first + if (p.rawIndex > (p.raw.size() - sizeof(uint8_t))) + { + return 1; + } + uint8_t len = p.raw[p.rawIndex++]; + // check to see that there are n bytes left + auto [first, last] = p.pop<char>(len); + if (first == last) + { + return 1; + } + t.reserve(last - first); + t.insert(0, first, (last - first)); + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for fixed_uint_t types + */ +template <unsigned N> +struct UnpackSingle<fixed_uint_t<N>> +{ + static int op(Payload& p, fixed_uint_t<N>& t) + { + static_assert(N <= (details::bitStreamSize - CHAR_BIT)); + constexpr size_t count = N; + // acquire enough bits in the stream to fulfill the Payload + if (p.fillBits(count)) + { + return -1; + } + fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1); + t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>(); + p.bitStream >>= count; + p.bitCount -= count; + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for bool. */ +template <> +struct UnpackSingle<bool> +{ + static int op(Payload& p, bool& b) + { + // acquire enough bits in the stream to fulfill the Payload + if (p.fillBits(1)) + { + return -1; + } + b = static_cast<bool>(p.bitStream & 0x01); + // clear bits from stream + p.bitStream >>= 1; + p.bitCount -= 1; + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for std::bitset<N> + */ +template <size_t N> +struct UnpackSingle<std::bitset<N>> +{ + static int op(Payload& p, std::bitset<N>& t) + { + static_assert(N <= (details::bitStreamSize - CHAR_BIT)); + size_t count = N; + // acquire enough bits in the stream to fulfill the Payload + if (p.fillBits(count)) + { + return -1; + } + fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1); + t |= (p.bitStream & bitmask).convert_to<unsigned long long>(); + p.bitStream >>= count; + p.bitCount -= count; + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for std::optional<T> */ +template <typename T> +struct UnpackSingle<std::optional<T>> +{ + static int op(Payload& p, std::optional<T>& t) + { + bool priorError = p.unpackError; + size_t priorIndex = p.rawIndex; + // more stuff to unroll if partial bytes are out + size_t priorBitCount = p.bitCount; + fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream; + t.emplace(); + int ret = UnpackSingle<T>::op(p, *t); + if (ret != 0) + { + t.reset(); + p.rawIndex = priorIndex; + p.bitStream = priorBits; + p.bitCount = priorBitCount; + p.unpackError = priorError; + } + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for std::array<T, N> */ +template <typename T, size_t N> +struct UnpackSingle<std::array<T, N>> +{ + static int op(Payload& p, std::array<T, N>& t) + { + int ret = 0; + size_t priorIndex = p.rawIndex; + for (auto& v : t) + { + ret = UnpackSingle<T>::op(p, v); + if (ret) + { + p.rawIndex = priorIndex; + t = std::array<T, N>(); + break; + } + } + return ret; + } +}; + +/** @brief Specialization of UnpackSingle for std::array<uint8_t> */ +template <size_t N> +struct UnpackSingle<std::array<uint8_t, N>> +{ + static int op(Payload& p, std::array<uint8_t, N>& t) + { + if (p.raw.size() - p.rawIndex < N) + { + t.fill(0); + return -1; + } + // copy out the bytes + std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N, + t.begin()); + p.rawIndex += N; + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for std::vector<T> */ +template <typename T> +struct UnpackSingle<std::vector<T>> +{ + static int op(Payload& p, std::vector<T>& t) + { + int ret = 0; + while (p.rawIndex < p.raw.size()) + { + t.emplace_back(); + ret = UnpackSingle<T>::op(p, t.back()); + if (ret) + { + t.pop_back(); + break; + } + } + return ret; + } +}; + +/** @brief Specialization of UnpackSingle for std::vector<uint8_t> */ +template <> +struct UnpackSingle<std::vector<uint8_t>> +{ + static int op(Payload& p, std::vector<uint8_t>& t) + { + // copy out the remainder of the message + t.reserve(p.raw.size() - p.rawIndex); + t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end()); + p.rawIndex = p.raw.size(); + return 0; + } +}; + +/** @brief Specialization of UnpackSingle for Payload */ +template <> +struct UnpackSingle<Payload> +{ + static int op(Payload& p, Payload& t) + { + // mark that this payload is being included in the args + p.trailingOk = true; + t = p; + // reset the unpacking flags so it can be properly checked + t.trailingOk = false; + t.unpackCheck = true; + t.unpackError = false; + return 0; + } +}; + +} // namespace details + +} // namespace message + +} // namespace ipmi |