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 
18e7329c71SVernon Mauery #include <ipmid/message/types.hpp>
19fbc6c9d7SPatrick Williams #include <phosphor-logging/log.hpp>
20fbc6c9d7SPatrick Williams 
21fbc6c9d7SPatrick Williams #include <array>
22e7329c71SVernon Mauery #include <memory>
23bae91350SVernon Mauery #include <optional>
24*c329ceeaSWilly Tu #include <span>
25e2aec26cSWilliam A. Kennington III #include <string_view>
26e7329c71SVernon Mauery #include <tuple>
27e7329c71SVernon Mauery #include <utility>
2800b096d1SAlexander Amelkin #include <variant>
29e7329c71SVernon Mauery #include <vector>
30e7329c71SVernon Mauery 
31e7329c71SVernon Mauery namespace ipmi
32e7329c71SVernon Mauery {
33e7329c71SVernon Mauery 
34e7329c71SVernon Mauery namespace message
35e7329c71SVernon Mauery {
36e7329c71SVernon Mauery 
37e7329c71SVernon Mauery namespace details
38e7329c71SVernon Mauery {
39e7329c71SVernon Mauery 
40e7329c71SVernon Mauery /**************************************
41e7329c71SVernon Mauery  * ipmi return type helpers
42e7329c71SVernon Mauery  **************************************/
43e7329c71SVernon Mauery 
44e7329c71SVernon Mauery template <typename NumericType, size_t byteIndex = 0>
PackBytes(uint8_t * pointer,const NumericType & i)45e7329c71SVernon Mauery void PackBytes(uint8_t* pointer, const NumericType& i)
46e7329c71SVernon Mauery {
47e7329c71SVernon Mauery     if constexpr (byteIndex < sizeof(NumericType))
48e7329c71SVernon Mauery     {
49e7329c71SVernon Mauery         *pointer = static_cast<uint8_t>(i >> (8 * byteIndex));
50e7329c71SVernon Mauery         PackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
51e7329c71SVernon Mauery     }
52e7329c71SVernon Mauery }
53e7329c71SVernon Mauery 
54e7329c71SVernon Mauery template <typename NumericType, size_t byteIndex = 0>
PackBytesUnaligned(Payload & p,const NumericType & i)55e7329c71SVernon Mauery void PackBytesUnaligned(Payload& p, const NumericType& i)
56e7329c71SVernon Mauery {
57e7329c71SVernon Mauery     if constexpr (byteIndex < sizeof(NumericType))
58e7329c71SVernon Mauery     {
59e7329c71SVernon Mauery         p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex)));
60e7329c71SVernon Mauery         PackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
61e7329c71SVernon Mauery     }
62e7329c71SVernon Mauery }
63e7329c71SVernon Mauery 
64e7329c71SVernon Mauery /** @struct PackSingle
65e7329c71SVernon Mauery  *  @brief Utility to pack a single C++ element into a Payload
66e7329c71SVernon Mauery  *
67e7329c71SVernon Mauery  *  User-defined types are expected to specialize this template in order to
68e7329c71SVernon Mauery  *  get their functionality.
69e7329c71SVernon Mauery  *
70e7329c71SVernon Mauery  *  @tparam S - Type of element to pack.
71e7329c71SVernon Mauery  */
72e7329c71SVernon Mauery template <typename T>
73e7329c71SVernon Mauery struct PackSingle
74e7329c71SVernon Mauery {
75e7329c71SVernon Mauery     /** @brief Do the operation to pack element.
76e7329c71SVernon Mauery      *
77e7329c71SVernon Mauery      *  @param[in] p - Payload to pack into.
78e7329c71SVernon Mauery      *  @param[out] t - The reference to pack item into.
79e7329c71SVernon Mauery      */
opipmi::message::details::PackSingle80f299807fSJames Feist     static int op(Payload& p, const T& t)
81e7329c71SVernon Mauery     {
82336405b8SVernon Mauery         static_assert(std::is_integral_v<T>,
83336405b8SVernon Mauery                       "Attempt to pack a type that has no IPMI pack operation");
84e7329c71SVernon Mauery         // if not on a byte boundary, must pack values LSbit/LSByte first
85e7329c71SVernon Mauery         if (p.bitCount)
86e7329c71SVernon Mauery         {
87e7329c71SVernon Mauery             PackBytesUnaligned<T>(p, t);
88e7329c71SVernon Mauery         }
89e7329c71SVernon Mauery         else
90e7329c71SVernon Mauery         {
91e7329c71SVernon Mauery             // copy in bits to vector....
92e7329c71SVernon Mauery             p.raw.resize(p.raw.size() + sizeof(T));
93e7329c71SVernon Mauery             uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T);
94e7329c71SVernon Mauery             PackBytes<T>(out, t);
95e7329c71SVernon Mauery         }
96e7329c71SVernon Mauery         return 0;
97e7329c71SVernon Mauery     }
98e7329c71SVernon Mauery };
99e7329c71SVernon Mauery 
100508c4576SVernon Mauery /** @brief Specialization of PackSingle for std::tuple<T> */
101508c4576SVernon Mauery template <typename... T>
102508c4576SVernon Mauery struct PackSingle<std::tuple<T...>>
103508c4576SVernon Mauery {
opipmi::message::details::PackSingle104508c4576SVernon Mauery     static int op(Payload& p, const std::tuple<T...>& v)
105508c4576SVernon Mauery     {
106508c4576SVernon Mauery         return std::apply([&p](const T&... args) { return p.pack(args...); },
107508c4576SVernon Mauery                           v);
108508c4576SVernon Mauery     }
109508c4576SVernon Mauery };
110508c4576SVernon Mauery 
111e7329c71SVernon Mauery /** @brief Specialization of PackSingle for std::string
112e7329c71SVernon Mauery  *  represented as a UCSD-Pascal style string
113e7329c71SVernon Mauery  */
114e7329c71SVernon Mauery template <>
115e7329c71SVernon Mauery struct PackSingle<std::string>
116e7329c71SVernon Mauery {
opipmi::message::details::PackSingle117f299807fSJames Feist     static int op(Payload& p, const std::string& t)
118e7329c71SVernon Mauery     {
119e7329c71SVernon Mauery         // check length first
120e7329c71SVernon Mauery         uint8_t len;
121e7329c71SVernon Mauery         if (t.length() > std::numeric_limits<decltype(len)>::max())
122e7329c71SVernon Mauery         {
123e7329c71SVernon Mauery             using namespace phosphor::logging;
124e7329c71SVernon Mauery             log<level::ERR>("long string truncated on IPMI message pack");
125e7329c71SVernon Mauery             return 1;
126e7329c71SVernon Mauery         }
127e7329c71SVernon Mauery         len = static_cast<uint8_t>(t.length());
128e7329c71SVernon Mauery         PackSingle<uint8_t>::op(p, len);
129e7329c71SVernon Mauery         p.append(t.c_str(), t.c_str() + t.length());
130e7329c71SVernon Mauery         return 0;
131e7329c71SVernon Mauery     }
132e7329c71SVernon Mauery };
133e7329c71SVernon Mauery 
134e7329c71SVernon Mauery /** @brief Specialization of PackSingle for fixed_uint_t types
135e7329c71SVernon Mauery  */
136b4905919STim Lee template <bitcount_t N>
137e7329c71SVernon Mauery struct PackSingle<fixed_uint_t<N>>
138e7329c71SVernon Mauery {
opipmi::message::details::PackSingle139f299807fSJames Feist     static int op(Payload& p, const fixed_uint_t<N>& t)
140e7329c71SVernon Mauery     {
141e7329c71SVernon Mauery         size_t count = N;
142e7329c71SVernon Mauery         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
14330f88f02SLei YU         static_assert(N <= std::numeric_limits<uint64_t>::digits,
14430f88f02SLei YU                       "Type exceeds uint64_t limit");
14530f88f02SLei YU         uint64_t bits = static_cast<uint64_t>(t);
146e7329c71SVernon Mauery         while (count > 0)
147e7329c71SVernon Mauery         {
148e7329c71SVernon Mauery             size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT));
149e7329c71SVernon Mauery             p.appendBits(appendCount, static_cast<uint8_t>(bits));
150e7329c71SVernon Mauery             bits >>= CHAR_BIT;
151e7329c71SVernon Mauery             count -= appendCount;
152e7329c71SVernon Mauery         }
153e7329c71SVernon Mauery         return 0;
154e7329c71SVernon Mauery     }
155e7329c71SVernon Mauery };
156e7329c71SVernon Mauery 
157e7329c71SVernon Mauery /** @brief Specialization of PackSingle for bool. */
158e7329c71SVernon Mauery template <>
159e7329c71SVernon Mauery struct PackSingle<bool>
160e7329c71SVernon Mauery {
opipmi::message::details::PackSingle161f299807fSJames Feist     static int op(Payload& p, const bool& b)
162e7329c71SVernon Mauery     {
163e7329c71SVernon Mauery         p.appendBits(1, b);
164e7329c71SVernon Mauery         return 0;
165e7329c71SVernon Mauery     }
166e7329c71SVernon Mauery };
167e7329c71SVernon Mauery 
168e7329c71SVernon Mauery /** @brief Specialization of PackSingle for std::bitset<N> */
169e7329c71SVernon Mauery template <size_t N>
170e7329c71SVernon Mauery struct PackSingle<std::bitset<N>>
171e7329c71SVernon Mauery {
opipmi::message::details::PackSingle172f299807fSJames Feist     static int op(Payload& p, const std::bitset<N>& t)
173e7329c71SVernon Mauery     {
174e7329c71SVernon Mauery         size_t count = N;
175e7329c71SVernon Mauery         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
176e7329c71SVernon Mauery         unsigned long long bits = t.to_ullong();
177e7329c71SVernon Mauery         while (count > 0)
178e7329c71SVernon Mauery         {
179e7329c71SVernon Mauery             size_t appendCount = std::min(count, size_t(CHAR_BIT));
180e7329c71SVernon Mauery             p.appendBits(appendCount, static_cast<uint8_t>(bits));
181e7329c71SVernon Mauery             bits >>= CHAR_BIT;
182e7329c71SVernon Mauery             count -= appendCount;
183e7329c71SVernon Mauery         }
184e7329c71SVernon Mauery         return 0;
185e7329c71SVernon Mauery     }
186e7329c71SVernon Mauery };
187e7329c71SVernon Mauery 
188bae91350SVernon Mauery /** @brief Specialization of PackSingle for std::optional<T> */
189bae91350SVernon Mauery template <typename T>
190bae91350SVernon Mauery struct PackSingle<std::optional<T>>
191bae91350SVernon Mauery {
opipmi::message::details::PackSingle192bae91350SVernon Mauery     static int op(Payload& p, const std::optional<T>& t)
193bae91350SVernon Mauery     {
194bae91350SVernon Mauery         int ret = 0;
195bae91350SVernon Mauery         if (t)
196bae91350SVernon Mauery         {
197bae91350SVernon Mauery             ret = PackSingle<T>::op(p, *t);
198bae91350SVernon Mauery         }
199bae91350SVernon Mauery         return ret;
200bae91350SVernon Mauery     }
201bae91350SVernon Mauery };
202bae91350SVernon Mauery 
203e7329c71SVernon Mauery /** @brief Specialization of PackSingle for std::array<T, N> */
204e7329c71SVernon Mauery template <typename T, size_t N>
205e7329c71SVernon Mauery struct PackSingle<std::array<T, N>>
206e7329c71SVernon Mauery {
opipmi::message::details::PackSingle207f299807fSJames Feist     static int op(Payload& p, const std::array<T, N>& t)
208e7329c71SVernon Mauery     {
209e7329c71SVernon Mauery         int ret = 0;
210f299807fSJames Feist         for (const auto& v : t)
211e7329c71SVernon Mauery         {
212e7329c71SVernon Mauery             int ret = PackSingle<T>::op(p, v);
213e7329c71SVernon Mauery             if (ret)
214e7329c71SVernon Mauery             {
215e7329c71SVernon Mauery                 break;
216e7329c71SVernon Mauery             }
217e7329c71SVernon Mauery         }
218e7329c71SVernon Mauery         return ret;
219e7329c71SVernon Mauery     }
220e7329c71SVernon Mauery };
221e7329c71SVernon Mauery 
222e7329c71SVernon Mauery /** @brief Specialization of PackSingle for std::vector<T> */
223e7329c71SVernon Mauery template <typename T>
224e7329c71SVernon Mauery struct PackSingle<std::vector<T>>
225e7329c71SVernon Mauery {
opipmi::message::details::PackSingle226f299807fSJames Feist     static int op(Payload& p, const std::vector<T>& t)
227e7329c71SVernon Mauery     {
228e7329c71SVernon Mauery         int ret = 0;
229f299807fSJames Feist         for (const auto& v : t)
230e7329c71SVernon Mauery         {
231e7329c71SVernon Mauery             int ret = PackSingle<T>::op(p, v);
232e7329c71SVernon Mauery             if (ret)
233e7329c71SVernon Mauery             {
234e7329c71SVernon Mauery                 break;
235e7329c71SVernon Mauery             }
236e7329c71SVernon Mauery         }
237e7329c71SVernon Mauery         return ret;
238e7329c71SVernon Mauery     }
239e7329c71SVernon Mauery };
240e7329c71SVernon Mauery 
241e7329c71SVernon Mauery /** @brief Specialization of PackSingle for std::vector<uint8_t> */
242e7329c71SVernon Mauery template <>
243e7329c71SVernon Mauery struct PackSingle<std::vector<uint8_t>>
244e7329c71SVernon Mauery {
opipmi::message::details::PackSingle245f299807fSJames Feist     static int op(Payload& p, const std::vector<uint8_t>& t)
246e7329c71SVernon Mauery     {
247906e0f80SWilliam A. Kennington III         if (p.bitCount != 0)
248906e0f80SWilliam A. Kennington III         {
249906e0f80SWilliam A. Kennington III             return 1;
250906e0f80SWilliam A. Kennington III         }
251e7329c71SVernon Mauery         p.raw.reserve(p.raw.size() + t.size());
252e7329c71SVernon Mauery         p.raw.insert(p.raw.end(), t.begin(), t.end());
253e7329c71SVernon Mauery         return 0;
254e7329c71SVernon Mauery     }
255e7329c71SVernon Mauery };
256e7329c71SVernon Mauery 
257997952afSVernon Mauery /** @brief Specialization of PackSingle for SecureBuffer */
258997952afSVernon Mauery template <>
259997952afSVernon Mauery struct PackSingle<SecureBuffer>
260997952afSVernon Mauery {
opipmi::message::details::PackSingle261997952afSVernon Mauery     static int op(Payload& p, const SecureBuffer& t)
262997952afSVernon Mauery     {
263997952afSVernon Mauery         if (p.bitCount != 0)
264997952afSVernon Mauery         {
265997952afSVernon Mauery             return 1;
266997952afSVernon Mauery         }
267997952afSVernon Mauery         p.raw.reserve(p.raw.size() + t.size());
268997952afSVernon Mauery         p.raw.insert(p.raw.end(), t.begin(), t.end());
269997952afSVernon Mauery         return 0;
270997952afSVernon Mauery     }
271997952afSVernon Mauery };
272997952afSVernon Mauery 
273*c329ceeaSWilly Tu /** @brief Specialization of PackSingle for std::span<const uint8_t> */
274*c329ceeaSWilly Tu template <>
275*c329ceeaSWilly Tu struct PackSingle<std::span<const uint8_t>>
276*c329ceeaSWilly Tu {
opipmi::message::details::PackSingle277*c329ceeaSWilly Tu     static int op(Payload& p, const std::span<const uint8_t>& t)
278*c329ceeaSWilly Tu     {
279*c329ceeaSWilly Tu         if (p.bitCount != 0)
280*c329ceeaSWilly Tu         {
281*c329ceeaSWilly Tu             return 1;
282*c329ceeaSWilly Tu         }
283*c329ceeaSWilly Tu         p.raw.reserve(p.raw.size() + t.size());
284*c329ceeaSWilly Tu         p.raw.insert(p.raw.end(), t.begin(), t.end());
285*c329ceeaSWilly Tu         return 0;
286*c329ceeaSWilly Tu     }
287*c329ceeaSWilly Tu };
288*c329ceeaSWilly Tu 
289e2aec26cSWilliam A. Kennington III /** @brief Specialization of PackSingle for std::string_view */
290e2aec26cSWilliam A. Kennington III template <>
291e2aec26cSWilliam A. Kennington III struct PackSingle<std::string_view>
292e2aec26cSWilliam A. Kennington III {
opipmi::message::details::PackSingle293e2aec26cSWilliam A. Kennington III     static int op(Payload& p, const std::string_view& t)
294e2aec26cSWilliam A. Kennington III     {
295e2aec26cSWilliam A. Kennington III         if (p.bitCount != 0)
296e2aec26cSWilliam A. Kennington III         {
297e2aec26cSWilliam A. Kennington III             return 1;
298e2aec26cSWilliam A. Kennington III         }
299e2aec26cSWilliam A. Kennington III         p.raw.reserve(p.raw.size() + t.size());
300e2aec26cSWilliam A. Kennington III         p.raw.insert(p.raw.end(), t.begin(), t.end());
301e2aec26cSWilliam A. Kennington III         return 0;
302e2aec26cSWilliam A. Kennington III     }
303e2aec26cSWilliam A. Kennington III };
304e2aec26cSWilliam A. Kennington III 
305f299807fSJames Feist /** @brief Specialization of PackSingle for std::variant<T, N> */
306f299807fSJames Feist template <typename... T>
307f299807fSJames Feist struct PackSingle<std::variant<T...>>
308f299807fSJames Feist {
opipmi::message::details::PackSingle309f299807fSJames Feist     static int op(Payload& p, const std::variant<T...>& v)
310f299807fSJames Feist     {
311f299807fSJames Feist         return std::visit(
312f299807fSJames Feist             [&p](const auto& arg) {
313f299807fSJames Feist                 return PackSingle<std::decay_t<decltype(arg)>>::op(p, arg);
314f299807fSJames Feist             },
315f299807fSJames Feist             v);
316f299807fSJames Feist     }
317f299807fSJames Feist };
318f299807fSJames Feist 
319e15e53ebSWilliam A. Kennington III /** @brief Specialization of PackSingle for Payload */
320e15e53ebSWilliam A. Kennington III template <>
321e15e53ebSWilliam A. Kennington III struct PackSingle<Payload>
322e15e53ebSWilliam A. Kennington III {
opipmi::message::details::PackSingle323e15e53ebSWilliam A. Kennington III     static int op(Payload& p, const Payload& t)
324e15e53ebSWilliam A. Kennington III     {
325e15e53ebSWilliam A. Kennington III         if (p.bitCount != 0 || t.bitCount != 0)
326e15e53ebSWilliam A. Kennington III         {
327e15e53ebSWilliam A. Kennington III             return 1;
328e15e53ebSWilliam A. Kennington III         }
329e15e53ebSWilliam A. Kennington III         p.raw.reserve(p.raw.size() + t.raw.size());
330e15e53ebSWilliam A. Kennington III         p.raw.insert(p.raw.end(), t.raw.begin(), t.raw.end());
331e15e53ebSWilliam A. Kennington III         return 0;
332e15e53ebSWilliam A. Kennington III     }
333e15e53ebSWilliam A. Kennington III };
334e15e53ebSWilliam A. Kennington III 
335e7329c71SVernon Mauery } // namespace details
336e7329c71SVernon Mauery 
337e7329c71SVernon Mauery } // namespace message
338e7329c71SVernon Mauery 
339e7329c71SVernon Mauery } // namespace ipmi
340