summaryrefslogtreecommitdiffstats
path: root/include/ipmid/message/unpack.hpp
diff options
context:
space:
mode:
authorVernon Mauery <vernon.mauery@linux.intel.com>2018-10-08 12:05:16 -0700
committerVernon Mauery <vernon.mauery@linux.intel.com>2019-02-25 14:27:21 -0800
commite7329c71f3d22e010c38a7f738e81ab78330038e (patch)
tree8cb355cbde96d7a341f1c707c7f0e8c334f88e58 /include/ipmid/message/unpack.hpp
parent1bb0c7fc55b21bb40b7afb567f2f938f15411ca1 (diff)
downloadphosphor-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.hpp340
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
OpenPOWER on IntegriCloud