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 <memory>
21 #include <optional>
22 #include <phosphor-logging/log.hpp>
23 #include <string_view>
24 #include <tuple>
25 #include <utility>
26 #include <variant>
27 #include <vector>
28 
29 namespace ipmi
30 {
31 
32 namespace message
33 {
34 
35 namespace details
36 {
37 
38 /**************************************
39  * ipmi return type helpers
40  **************************************/
41 
42 template <typename NumericType, size_t byteIndex = 0>
43 void PackBytes(uint8_t* pointer, const NumericType& i)
44 {
45     if constexpr (byteIndex < sizeof(NumericType))
46     {
47         *pointer = static_cast<uint8_t>(i >> (8 * byteIndex));
48         PackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
49     }
50 }
51 
52 template <typename NumericType, size_t byteIndex = 0>
53 void PackBytesUnaligned(Payload& p, const NumericType& i)
54 {
55     if constexpr (byteIndex < sizeof(NumericType))
56     {
57         p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex)));
58         PackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
59     }
60 }
61 
62 /** @struct PackSingle
63  *  @brief Utility to pack a single C++ element into a Payload
64  *
65  *  User-defined types are expected to specialize this template in order to
66  *  get their functionality.
67  *
68  *  @tparam S - Type of element to pack.
69  */
70 template <typename T>
71 struct PackSingle
72 {
73     /** @brief Do the operation to pack element.
74      *
75      *  @param[in] p - Payload to pack into.
76      *  @param[out] t - The reference to pack item into.
77      */
78     static int op(Payload& p, const T& t)
79     {
80         static_assert(std::is_integral_v<T>,
81                       "Attempt to pack a type that has no IPMI pack operation");
82         // if not on a byte boundary, must pack values LSbit/LSByte first
83         if (p.bitCount)
84         {
85             PackBytesUnaligned<T>(p, t);
86         }
87         else
88         {
89             // copy in bits to vector....
90             p.raw.resize(p.raw.size() + sizeof(T));
91             uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T);
92             PackBytes<T>(out, t);
93         }
94         return 0;
95     }
96 };
97 
98 /** @brief Specialization of PackSingle for std::tuple<T> */
99 template <typename... T>
100 struct PackSingle<std::tuple<T...>>
101 {
102     static int op(Payload& p, const std::tuple<T...>& v)
103     {
104         return std::apply([&p](const T&... args) { return p.pack(args...); },
105                           v);
106     }
107 };
108 
109 /** @brief Specialization of PackSingle for std::string
110  *  represented as a UCSD-Pascal style string
111  */
112 template <>
113 struct PackSingle<std::string>
114 {
115     static int op(Payload& p, const std::string& t)
116     {
117         // check length first
118         uint8_t len;
119         if (t.length() > std::numeric_limits<decltype(len)>::max())
120         {
121             using namespace phosphor::logging;
122             log<level::ERR>("long string truncated on IPMI message pack");
123             return 1;
124         }
125         len = static_cast<uint8_t>(t.length());
126         PackSingle<uint8_t>::op(p, len);
127         p.append(t.c_str(), t.c_str() + t.length());
128         return 0;
129     }
130 };
131 
132 /** @brief Specialization of PackSingle for fixed_uint_t types
133  */
134 template <unsigned N>
135 struct PackSingle<fixed_uint_t<N>>
136 {
137     static int op(Payload& p, const fixed_uint_t<N>& t)
138     {
139         size_t count = N;
140         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
141         uint64_t bits = t;
142         while (count > 0)
143         {
144             size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT));
145             p.appendBits(appendCount, static_cast<uint8_t>(bits));
146             bits >>= CHAR_BIT;
147             count -= appendCount;
148         }
149         return 0;
150     }
151 };
152 
153 /** @brief Specialization of PackSingle for bool. */
154 template <>
155 struct PackSingle<bool>
156 {
157     static int op(Payload& p, const bool& b)
158     {
159         p.appendBits(1, b);
160         return 0;
161     }
162 };
163 
164 /** @brief Specialization of PackSingle for std::bitset<N> */
165 template <size_t N>
166 struct PackSingle<std::bitset<N>>
167 {
168     static int op(Payload& p, const std::bitset<N>& t)
169     {
170         size_t count = N;
171         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
172         unsigned long long bits = t.to_ullong();
173         while (count > 0)
174         {
175             size_t appendCount = std::min(count, size_t(CHAR_BIT));
176             p.appendBits(appendCount, static_cast<uint8_t>(bits));
177             bits >>= CHAR_BIT;
178             count -= appendCount;
179         }
180         return 0;
181     }
182 };
183 
184 /** @brief Specialization of PackSingle for std::optional<T> */
185 template <typename T>
186 struct PackSingle<std::optional<T>>
187 {
188     static int op(Payload& p, const std::optional<T>& t)
189     {
190         int ret = 0;
191         if (t)
192         {
193             ret = PackSingle<T>::op(p, *t);
194         }
195         return ret;
196     }
197 };
198 
199 /** @brief Specialization of PackSingle for std::array<T, N> */
200 template <typename T, size_t N>
201 struct PackSingle<std::array<T, N>>
202 {
203     static int op(Payload& p, const std::array<T, N>& t)
204     {
205         int ret = 0;
206         for (const auto& v : t)
207         {
208             int ret = PackSingle<T>::op(p, v);
209             if (ret)
210             {
211                 break;
212             }
213         }
214         return ret;
215     }
216 };
217 
218 /** @brief Specialization of PackSingle for std::vector<T> */
219 template <typename T>
220 struct PackSingle<std::vector<T>>
221 {
222     static int op(Payload& p, const std::vector<T>& t)
223     {
224         int ret = 0;
225         for (const auto& v : t)
226         {
227             int ret = PackSingle<T>::op(p, v);
228             if (ret)
229             {
230                 break;
231             }
232         }
233         return ret;
234     }
235 };
236 
237 /** @brief Specialization of PackSingle for std::vector<uint8_t> */
238 template <>
239 struct PackSingle<std::vector<uint8_t>>
240 {
241     static int op(Payload& p, const std::vector<uint8_t>& t)
242     {
243         if (p.bitCount != 0)
244         {
245             return 1;
246         }
247         p.raw.reserve(p.raw.size() + t.size());
248         p.raw.insert(p.raw.end(), t.begin(), t.end());
249         return 0;
250     }
251 };
252 
253 /** @brief Specialization of PackSingle for std::string_view */
254 template <>
255 struct PackSingle<std::string_view>
256 {
257     static int op(Payload& p, const std::string_view& t)
258     {
259         if (p.bitCount != 0)
260         {
261             return 1;
262         }
263         p.raw.reserve(p.raw.size() + t.size());
264         p.raw.insert(p.raw.end(), t.begin(), t.end());
265         return 0;
266     }
267 };
268 
269 /** @brief Specialization of PackSingle for std::variant<T, N> */
270 template <typename... T>
271 struct PackSingle<std::variant<T...>>
272 {
273     static int op(Payload& p, const std::variant<T...>& v)
274     {
275         return std::visit(
276             [&p](const auto& arg) {
277                 return PackSingle<std::decay_t<decltype(arg)>>::op(p, arg);
278             },
279             v);
280     }
281 };
282 
283 /** @brief Specialization of PackSingle for Payload */
284 template <>
285 struct PackSingle<Payload>
286 {
287     static int op(Payload& p, const Payload& t)
288     {
289         if (p.bitCount != 0 || t.bitCount != 0)
290         {
291             return 1;
292         }
293         p.raw.reserve(p.raw.size() + t.raw.size());
294         p.raw.insert(p.raw.end(), t.raw.begin(), t.raw.end());
295         return 0;
296     }
297 };
298 
299 } // namespace details
300 
301 } // namespace message
302 
303 } // namespace ipmi
304