1e7329c71SVernon Mauery /**
2e7329c71SVernon Mauery * Copyright © 2018 Intel Corporation
3e7329c71SVernon Mauery *
4e7329c71SVernon Mauery * Licensed under the Apache License, Version 2.0 (the "License");
5e7329c71SVernon Mauery * you may not use this file except in compliance with the License.
6e7329c71SVernon Mauery * You may obtain a copy of the License at
7e7329c71SVernon Mauery *
8e7329c71SVernon Mauery * http://www.apache.org/licenses/LICENSE-2.0
9e7329c71SVernon Mauery *
10e7329c71SVernon Mauery * Unless required by applicable law or agreed to in writing, software
11e7329c71SVernon Mauery * distributed under the License is distributed on an "AS IS" BASIS,
12e7329c71SVernon Mauery * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e7329c71SVernon Mauery * See the License for the specific language governing permissions and
14e7329c71SVernon Mauery * limitations under the License.
15e7329c71SVernon Mauery */
16e7329c71SVernon Mauery #pragma once
17e7329c71SVernon Mauery
18*9706bc8dSJayanth Othayoth #include <ipmid/message.hpp>
19e7329c71SVernon Mauery #include <ipmid/message/types.hpp>
20fbc6c9d7SPatrick Williams
21fbc6c9d7SPatrick Williams #include <array>
22e7329c71SVernon Mauery #include <optional>
23c329ceeaSWilly Tu #include <span>
24e7329c71SVernon Mauery #include <string>
25e7329c71SVernon Mauery #include <tuple>
26e7329c71SVernon Mauery #include <vector>
27e7329c71SVernon Mauery
28e7329c71SVernon Mauery namespace ipmi
29e7329c71SVernon Mauery {
30e7329c71SVernon Mauery
31e7329c71SVernon Mauery namespace message
32e7329c71SVernon Mauery {
33e7329c71SVernon Mauery
34e7329c71SVernon Mauery namespace details
35e7329c71SVernon Mauery {
36e7329c71SVernon Mauery
37e7329c71SVernon Mauery /**************************************
38e7329c71SVernon Mauery * ipmi return type helpers
39e7329c71SVernon Mauery **************************************/
40e7329c71SVernon Mauery
41e7329c71SVernon Mauery template <typename NumericType, size_t byteIndex = 0>
UnpackBytes(uint8_t * pointer,NumericType & i)42e7329c71SVernon Mauery void UnpackBytes(uint8_t* pointer, NumericType& i)
43e7329c71SVernon Mauery {
44e7329c71SVernon Mauery if constexpr (byteIndex < sizeof(NumericType))
45e7329c71SVernon Mauery {
46e7329c71SVernon Mauery i |= static_cast<NumericType>(*pointer) << (CHAR_BIT * byteIndex);
47e7329c71SVernon Mauery UnpackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
48e7329c71SVernon Mauery }
49e7329c71SVernon Mauery }
50e7329c71SVernon Mauery
51e7329c71SVernon Mauery template <typename NumericType, size_t byteIndex = 0>
UnpackBytesUnaligned(Payload & p,NumericType & i)52e7329c71SVernon Mauery void UnpackBytesUnaligned(Payload& p, NumericType& i)
53e7329c71SVernon Mauery {
54e7329c71SVernon Mauery if constexpr (byteIndex < sizeof(NumericType))
55e7329c71SVernon Mauery {
56e7329c71SVernon Mauery i |= static_cast<NumericType>(p.popBits(CHAR_BIT))
57e7329c71SVernon Mauery << (CHAR_BIT * byteIndex);
58e7329c71SVernon Mauery UnpackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
59e7329c71SVernon Mauery }
60e7329c71SVernon Mauery }
61e7329c71SVernon Mauery
62e7329c71SVernon Mauery /** @struct UnpackSingle
63e7329c71SVernon Mauery * @brief Utility to unpack a single C++ element from a Payload
64e7329c71SVernon Mauery *
65e7329c71SVernon Mauery * User-defined types are expected to specialize this template in order to
66e7329c71SVernon Mauery * get their functionality.
67e7329c71SVernon Mauery *
68e7329c71SVernon Mauery * @tparam T - Type of element to unpack.
69e7329c71SVernon Mauery */
70e7329c71SVernon Mauery template <typename T>
71e7329c71SVernon Mauery struct UnpackSingle
72e7329c71SVernon Mauery {
73e7329c71SVernon Mauery /** @brief Do the operation to unpack element.
74e7329c71SVernon Mauery *
75e7329c71SVernon Mauery * @param[in] p - Payload to unpack from.
76e7329c71SVernon Mauery * @param[out] t - The reference to unpack item into.
77e7329c71SVernon Mauery */
opipmi::message::details::UnpackSingle78e7329c71SVernon Mauery static int op(Payload& p, T& t)
79e7329c71SVernon Mauery {
80e7329c71SVernon Mauery if constexpr (std::is_fundamental<T>::value)
81e7329c71SVernon Mauery {
82e7329c71SVernon Mauery t = 0;
83e7329c71SVernon Mauery if (p.bitCount)
84e7329c71SVernon Mauery {
85e7329c71SVernon Mauery if (p.fillBits(CHAR_BIT * sizeof(t)))
86e7329c71SVernon Mauery {
87e7329c71SVernon Mauery return 1;
88e7329c71SVernon Mauery }
89e7329c71SVernon Mauery UnpackBytesUnaligned<T>(p, t);
90e7329c71SVernon Mauery }
91e7329c71SVernon Mauery else
92e7329c71SVernon Mauery {
93e7329c71SVernon Mauery // copy out bits from vector....
94e7329c71SVernon Mauery if (p.raw.size() < (p.rawIndex + sizeof(t)))
95e7329c71SVernon Mauery {
96e7329c71SVernon Mauery return 1;
97e7329c71SVernon Mauery }
98e7329c71SVernon Mauery auto iter = p.raw.data() + p.rawIndex;
99e7329c71SVernon Mauery t = 0;
100e7329c71SVernon Mauery UnpackBytes<T>(iter, t);
101e7329c71SVernon Mauery p.rawIndex += sizeof(t);
102e7329c71SVernon Mauery }
103e7329c71SVernon Mauery return 0;
104e7329c71SVernon Mauery }
105a3dd7661SVernon Mauery else if constexpr (utility::is_tuple<T>::value)
106e7329c71SVernon Mauery {
107e7329c71SVernon Mauery bool priorError = p.unpackError;
108e7329c71SVernon Mauery size_t priorIndex = p.rawIndex;
109e7329c71SVernon Mauery // more stuff to unroll if partial bytes are out
110e7329c71SVernon Mauery size_t priorBitCount = p.bitCount;
111e7329c71SVernon Mauery fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
112e7329c71SVernon Mauery int ret = p.unpack(t);
113e7329c71SVernon Mauery if (ret != 0)
114e7329c71SVernon Mauery {
115e7329c71SVernon Mauery t = T();
116e7329c71SVernon Mauery p.rawIndex = priorIndex;
117e7329c71SVernon Mauery p.bitStream = priorBits;
118e7329c71SVernon Mauery p.bitCount = priorBitCount;
119e7329c71SVernon Mauery p.unpackError = priorError;
120e7329c71SVernon Mauery }
121caabc36bSVernon Mauery return ret;
122e7329c71SVernon Mauery }
123a3dd7661SVernon Mauery else
124a3dd7661SVernon Mauery {
125a3dd7661SVernon Mauery static_assert(
126a3dd7661SVernon Mauery utility::dependent_false<T>::value,
127a3dd7661SVernon Mauery "Attempt to unpack a type that has no IPMI unpack operation");
128e7329c71SVernon Mauery }
129e7329c71SVernon Mauery }
130e7329c71SVernon Mauery };
131e7329c71SVernon Mauery
132e7329c71SVernon Mauery /** @struct UnpackSingle
133e7329c71SVernon Mauery * @brief Utility to unpack a single C++ element from a Payload
134e7329c71SVernon Mauery *
135e7329c71SVernon Mauery * Specialization to unpack std::string represented as a
136e7329c71SVernon Mauery * UCSD-Pascal style string
137e7329c71SVernon Mauery */
138e7329c71SVernon Mauery template <>
139e7329c71SVernon Mauery struct UnpackSingle<std::string>
140e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle141e7329c71SVernon Mauery static int op(Payload& p, std::string& t)
142e7329c71SVernon Mauery {
143e7329c71SVernon Mauery // pop len first
144e7329c71SVernon Mauery if (p.rawIndex > (p.raw.size() - sizeof(uint8_t)))
145e7329c71SVernon Mauery {
146e7329c71SVernon Mauery return 1;
147e7329c71SVernon Mauery }
148e7329c71SVernon Mauery uint8_t len = p.raw[p.rawIndex++];
149e7329c71SVernon Mauery // check to see that there are n bytes left
150e7329c71SVernon Mauery auto [first, last] = p.pop<char>(len);
151e7329c71SVernon Mauery if (first == last)
152e7329c71SVernon Mauery {
153e7329c71SVernon Mauery return 1;
154e7329c71SVernon Mauery }
155e7329c71SVernon Mauery t.reserve(last - first);
156e7329c71SVernon Mauery t.insert(0, first, (last - first));
157e7329c71SVernon Mauery return 0;
158e7329c71SVernon Mauery }
159e7329c71SVernon Mauery };
160e7329c71SVernon Mauery
161e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for fixed_uint_t types
162e7329c71SVernon Mauery */
163b4905919STim Lee template <bitcount_t N>
164e7329c71SVernon Mauery struct UnpackSingle<fixed_uint_t<N>>
165e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle166e7329c71SVernon Mauery static int op(Payload& p, fixed_uint_t<N>& t)
167e7329c71SVernon Mauery {
168e7329c71SVernon Mauery static_assert(N <= (details::bitStreamSize - CHAR_BIT));
169e7329c71SVernon Mauery constexpr size_t count = N;
170e7329c71SVernon Mauery // acquire enough bits in the stream to fulfill the Payload
171e7329c71SVernon Mauery if (p.fillBits(count))
172e7329c71SVernon Mauery {
173e7329c71SVernon Mauery return -1;
174e7329c71SVernon Mauery }
175e7329c71SVernon Mauery fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
176e7329c71SVernon Mauery t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>();
177e7329c71SVernon Mauery p.bitStream >>= count;
178e7329c71SVernon Mauery p.bitCount -= count;
179e7329c71SVernon Mauery return 0;
180e7329c71SVernon Mauery }
181e7329c71SVernon Mauery };
182e7329c71SVernon Mauery
183e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for bool. */
184e7329c71SVernon Mauery template <>
185e7329c71SVernon Mauery struct UnpackSingle<bool>
186e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle187e7329c71SVernon Mauery static int op(Payload& p, bool& b)
188e7329c71SVernon Mauery {
189e7329c71SVernon Mauery // acquire enough bits in the stream to fulfill the Payload
190e7329c71SVernon Mauery if (p.fillBits(1))
191e7329c71SVernon Mauery {
192e7329c71SVernon Mauery return -1;
193e7329c71SVernon Mauery }
194e7329c71SVernon Mauery b = static_cast<bool>(p.bitStream & 0x01);
195e7329c71SVernon Mauery // clear bits from stream
196e7329c71SVernon Mauery p.bitStream >>= 1;
197e7329c71SVernon Mauery p.bitCount -= 1;
198e7329c71SVernon Mauery return 0;
199e7329c71SVernon Mauery }
200e7329c71SVernon Mauery };
201e7329c71SVernon Mauery
202e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::bitset<N>
203e7329c71SVernon Mauery */
204e7329c71SVernon Mauery template <size_t N>
205e7329c71SVernon Mauery struct UnpackSingle<std::bitset<N>>
206e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle207e7329c71SVernon Mauery static int op(Payload& p, std::bitset<N>& t)
208e7329c71SVernon Mauery {
209e7329c71SVernon Mauery static_assert(N <= (details::bitStreamSize - CHAR_BIT));
210e7329c71SVernon Mauery size_t count = N;
211e7329c71SVernon Mauery // acquire enough bits in the stream to fulfill the Payload
212e7329c71SVernon Mauery if (p.fillBits(count))
213e7329c71SVernon Mauery {
214e7329c71SVernon Mauery return -1;
215e7329c71SVernon Mauery }
2160d49e479SWilliam A. Kennington III fixed_uint_t<details::bitStreamSize> bitmask =
2170d49e479SWilliam A. Kennington III ~fixed_uint_t<details::bitStreamSize>(0) >>
2180d49e479SWilliam A. Kennington III (details::bitStreamSize - count);
219e7329c71SVernon Mauery t |= (p.bitStream & bitmask).convert_to<unsigned long long>();
220e7329c71SVernon Mauery p.bitStream >>= count;
221e7329c71SVernon Mauery p.bitCount -= count;
222e7329c71SVernon Mauery return 0;
223e7329c71SVernon Mauery }
224e7329c71SVernon Mauery };
225e7329c71SVernon Mauery
226e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::optional<T> */
227e7329c71SVernon Mauery template <typename T>
228e7329c71SVernon Mauery struct UnpackSingle<std::optional<T>>
229e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle230e7329c71SVernon Mauery static int op(Payload& p, std::optional<T>& t)
231e7329c71SVernon Mauery {
232e7329c71SVernon Mauery bool priorError = p.unpackError;
233e7329c71SVernon Mauery size_t priorIndex = p.rawIndex;
234e7329c71SVernon Mauery // more stuff to unroll if partial bytes are out
235e7329c71SVernon Mauery size_t priorBitCount = p.bitCount;
236e7329c71SVernon Mauery fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
237202702b0SJonathan Doman T value;
238202702b0SJonathan Doman int ret = UnpackSingle<T>::op(p, value);
239e7329c71SVernon Mauery if (ret != 0)
240e7329c71SVernon Mauery {
241e7329c71SVernon Mauery t.reset();
242e7329c71SVernon Mauery p.rawIndex = priorIndex;
243e7329c71SVernon Mauery p.bitStream = priorBits;
244e7329c71SVernon Mauery p.bitCount = priorBitCount;
245e7329c71SVernon Mauery p.unpackError = priorError;
246e7329c71SVernon Mauery }
247202702b0SJonathan Doman else
248202702b0SJonathan Doman {
249202702b0SJonathan Doman t.emplace(std::move(value));
250202702b0SJonathan Doman }
251e7329c71SVernon Mauery return 0;
252e7329c71SVernon Mauery }
253e7329c71SVernon Mauery };
254e7329c71SVernon Mauery
255e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::array<T, N> */
256e7329c71SVernon Mauery template <typename T, size_t N>
257e7329c71SVernon Mauery struct UnpackSingle<std::array<T, N>>
258e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle259e7329c71SVernon Mauery static int op(Payload& p, std::array<T, N>& t)
260e7329c71SVernon Mauery {
261e7329c71SVernon Mauery int ret = 0;
262e7329c71SVernon Mauery size_t priorIndex = p.rawIndex;
263e7329c71SVernon Mauery for (auto& v : t)
264e7329c71SVernon Mauery {
265e7329c71SVernon Mauery ret = UnpackSingle<T>::op(p, v);
266e7329c71SVernon Mauery if (ret)
267e7329c71SVernon Mauery {
268e7329c71SVernon Mauery p.rawIndex = priorIndex;
269e7329c71SVernon Mauery t = std::array<T, N>();
270e7329c71SVernon Mauery break;
271e7329c71SVernon Mauery }
272e7329c71SVernon Mauery }
273e7329c71SVernon Mauery return ret;
274e7329c71SVernon Mauery }
275e7329c71SVernon Mauery };
276e7329c71SVernon Mauery
277e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::array<uint8_t> */
278e7329c71SVernon Mauery template <size_t N>
279e7329c71SVernon Mauery struct UnpackSingle<std::array<uint8_t, N>>
280e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle281e7329c71SVernon Mauery static int op(Payload& p, std::array<uint8_t, N>& t)
282e7329c71SVernon Mauery {
283e7329c71SVernon Mauery if (p.raw.size() - p.rawIndex < N)
284e7329c71SVernon Mauery {
285e7329c71SVernon Mauery t.fill(0);
286e7329c71SVernon Mauery return -1;
287e7329c71SVernon Mauery }
288e7329c71SVernon Mauery // copy out the bytes
289e7329c71SVernon Mauery std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N,
290e7329c71SVernon Mauery t.begin());
291e7329c71SVernon Mauery p.rawIndex += N;
292e7329c71SVernon Mauery return 0;
293e7329c71SVernon Mauery }
294e7329c71SVernon Mauery };
295e7329c71SVernon Mauery
296e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::vector<T> */
297e7329c71SVernon Mauery template <typename T>
298e7329c71SVernon Mauery struct UnpackSingle<std::vector<T>>
299e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle300e7329c71SVernon Mauery static int op(Payload& p, std::vector<T>& t)
301e7329c71SVernon Mauery {
302e7329c71SVernon Mauery while (p.rawIndex < p.raw.size())
303e7329c71SVernon Mauery {
304e7329c71SVernon Mauery t.emplace_back();
305caabc36bSVernon Mauery if (UnpackSingle<T>::op(p, t.back()))
306e7329c71SVernon Mauery {
307e7329c71SVernon Mauery t.pop_back();
308e7329c71SVernon Mauery break;
309e7329c71SVernon Mauery }
310e7329c71SVernon Mauery }
311caabc36bSVernon Mauery // unpacking a vector is always successful:
312caabc36bSVernon Mauery // either stuff was unpacked successfully (return 0)
313caabc36bSVernon Mauery // or stuff was not unpacked, but should still return
314caabc36bSVernon Mauery // success because an empty vector or a not-fully-unpacked
315caabc36bSVernon Mauery // payload is not a failure.
316caabc36bSVernon Mauery return 0;
317e7329c71SVernon Mauery }
318e7329c71SVernon Mauery };
319e7329c71SVernon Mauery
320e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
321e7329c71SVernon Mauery template <>
322e7329c71SVernon Mauery struct UnpackSingle<std::vector<uint8_t>>
323e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle324e7329c71SVernon Mauery static int op(Payload& p, std::vector<uint8_t>& t)
325e7329c71SVernon Mauery {
326e7329c71SVernon Mauery // copy out the remainder of the message
327e7329c71SVernon Mauery t.reserve(p.raw.size() - p.rawIndex);
328e7329c71SVernon Mauery t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
329e7329c71SVernon Mauery p.rawIndex = p.raw.size();
330e7329c71SVernon Mauery return 0;
331e7329c71SVernon Mauery }
332e7329c71SVernon Mauery };
333e7329c71SVernon Mauery
334997952afSVernon Mauery /** @brief Specialization of UnpackSingle for SecureBuffer */
335997952afSVernon Mauery template <>
336997952afSVernon Mauery struct UnpackSingle<SecureBuffer>
337997952afSVernon Mauery {
opipmi::message::details::UnpackSingle338997952afSVernon Mauery static int op(Payload& p, SecureBuffer& t)
339997952afSVernon Mauery {
340997952afSVernon Mauery // copy out the remainder of the message
341997952afSVernon Mauery t.reserve(p.raw.size() - p.rawIndex);
342997952afSVernon Mauery t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
343997952afSVernon Mauery p.rawIndex = p.raw.size();
344997952afSVernon Mauery return 0;
345997952afSVernon Mauery }
346997952afSVernon Mauery };
347997952afSVernon Mauery
348c329ceeaSWilly Tu /** @brief Specialization of UnpackSingle for std::span<const uint8_t> */
349c329ceeaSWilly Tu template <>
350c329ceeaSWilly Tu struct UnpackSingle<std::span<const uint8_t>>
351c329ceeaSWilly Tu {
opipmi::message::details::UnpackSingle352c329ceeaSWilly Tu static int op(Payload& p, std::span<const uint8_t>& t)
353c329ceeaSWilly Tu {
354c329ceeaSWilly Tu // copy out the remainder of the message
355c329ceeaSWilly Tu t = std::span<const uint8_t>(p.raw.begin() + p.rawIndex, p.raw.end());
356c329ceeaSWilly Tu p.rawIndex = p.raw.size();
357c329ceeaSWilly Tu return 0;
358c329ceeaSWilly Tu }
359c329ceeaSWilly Tu };
360c329ceeaSWilly Tu
361e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for Payload */
362e7329c71SVernon Mauery template <>
363e7329c71SVernon Mauery struct UnpackSingle<Payload>
364e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle365e7329c71SVernon Mauery static int op(Payload& p, Payload& t)
366e7329c71SVernon Mauery {
36751694c22SWilliam A. Kennington III t = p;
368e7329c71SVernon Mauery // mark that this payload is being included in the args
369e7329c71SVernon Mauery p.trailingOk = true;
370e7329c71SVernon Mauery return 0;
371e7329c71SVernon Mauery }
372e7329c71SVernon Mauery };
373e7329c71SVernon Mauery
374e7329c71SVernon Mauery } // namespace details
375e7329c71SVernon Mauery
376e7329c71SVernon Mauery } // namespace message
377e7329c71SVernon Mauery
378e7329c71SVernon Mauery } // namespace ipmi
379