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 <optional>
21 #include <string>
22 #include <tuple>
23 #include <vector>
24 
25 namespace ipmi
26 {
27 
28 namespace message
29 {
30 
31 namespace details
32 {
33 
34 /**************************************
35  * ipmi return type helpers
36  **************************************/
37 
38 template <typename NumericType, size_t byteIndex = 0>
39 void UnpackBytes(uint8_t* pointer, NumericType& i)
40 {
41     if constexpr (byteIndex < sizeof(NumericType))
42     {
43         i |= static_cast<NumericType>(*pointer) << (CHAR_BIT * byteIndex);
44         UnpackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
45     }
46 }
47 
48 template <typename NumericType, size_t byteIndex = 0>
49 void UnpackBytesUnaligned(Payload& p, NumericType& i)
50 {
51     if constexpr (byteIndex < sizeof(NumericType))
52     {
53         i |= static_cast<NumericType>(p.popBits(CHAR_BIT))
54              << (CHAR_BIT * byteIndex);
55         UnpackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
56     }
57 }
58 
59 /** @struct UnpackSingle
60  *  @brief Utility to unpack a single C++ element from a Payload
61  *
62  *  User-defined types are expected to specialize this template in order to
63  *  get their functionality.
64  *
65  *  @tparam T - Type of element to unpack.
66  */
67 template <typename T>
68 struct UnpackSingle
69 {
70     /** @brief Do the operation to unpack element.
71      *
72      *  @param[in] p - Payload to unpack from.
73      *  @param[out] t - The reference to unpack item into.
74      */
75     static int op(Payload& p, T& t)
76     {
77         if constexpr (std::is_fundamental<T>::value)
78         {
79             t = 0;
80             if (p.bitCount)
81             {
82                 if (p.fillBits(CHAR_BIT * sizeof(t)))
83                 {
84                     return 1;
85                 }
86                 UnpackBytesUnaligned<T>(p, t);
87             }
88             else
89             {
90                 // copy out bits from vector....
91                 if (p.raw.size() < (p.rawIndex + sizeof(t)))
92                 {
93                     return 1;
94                 }
95                 auto iter = p.raw.data() + p.rawIndex;
96                 t = 0;
97                 UnpackBytes<T>(iter, t);
98                 p.rawIndex += sizeof(t);
99             }
100             return 0;
101         }
102         else if constexpr (utility::is_tuple<T>::value)
103         {
104             bool priorError = p.unpackError;
105             size_t priorIndex = p.rawIndex;
106             // more stuff to unroll if partial bytes are out
107             size_t priorBitCount = p.bitCount;
108             fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
109             int ret = p.unpack(t);
110             if (ret != 0)
111             {
112                 t = T();
113                 p.rawIndex = priorIndex;
114                 p.bitStream = priorBits;
115                 p.bitCount = priorBitCount;
116                 p.unpackError = priorError;
117             }
118             return ret;
119         }
120         else
121         {
122             static_assert(
123                 utility::dependent_false<T>::value,
124                 "Attempt to unpack a type that has no IPMI unpack operation");
125         }
126     }
127 };
128 
129 /** @struct UnpackSingle
130  *  @brief Utility to unpack a single C++ element from a Payload
131  *
132  *  Specialization to unpack std::string represented as a
133  *  UCSD-Pascal style string
134  */
135 template <>
136 struct UnpackSingle<std::string>
137 {
138     static int op(Payload& p, std::string& t)
139     {
140         // pop len first
141         if (p.rawIndex > (p.raw.size() - sizeof(uint8_t)))
142         {
143             return 1;
144         }
145         uint8_t len = p.raw[p.rawIndex++];
146         // check to see that there are n bytes left
147         auto [first, last] = p.pop<char>(len);
148         if (first == last)
149         {
150             return 1;
151         }
152         t.reserve(last - first);
153         t.insert(0, first, (last - first));
154         return 0;
155     }
156 };
157 
158 /** @brief Specialization of UnpackSingle for fixed_uint_t types
159  */
160 template <unsigned N>
161 struct UnpackSingle<fixed_uint_t<N>>
162 {
163     static int op(Payload& p, fixed_uint_t<N>& t)
164     {
165         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
166         constexpr size_t count = N;
167         // acquire enough bits in the stream to fulfill the Payload
168         if (p.fillBits(count))
169         {
170             return -1;
171         }
172         fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
173         t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>();
174         p.bitStream >>= count;
175         p.bitCount -= count;
176         return 0;
177     }
178 };
179 
180 /** @brief Specialization of UnpackSingle for bool. */
181 template <>
182 struct UnpackSingle<bool>
183 {
184     static int op(Payload& p, bool& b)
185     {
186         // acquire enough bits in the stream to fulfill the Payload
187         if (p.fillBits(1))
188         {
189             return -1;
190         }
191         b = static_cast<bool>(p.bitStream & 0x01);
192         // clear bits from stream
193         p.bitStream >>= 1;
194         p.bitCount -= 1;
195         return 0;
196     }
197 };
198 
199 /** @brief Specialization of UnpackSingle for std::bitset<N>
200  */
201 template <size_t N>
202 struct UnpackSingle<std::bitset<N>>
203 {
204     static int op(Payload& p, std::bitset<N>& t)
205     {
206         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
207         size_t count = N;
208         // acquire enough bits in the stream to fulfill the Payload
209         if (p.fillBits(count))
210         {
211             return -1;
212         }
213         fixed_uint_t<details::bitStreamSize> bitmask =
214             ~fixed_uint_t<details::bitStreamSize>(0) >>
215             (details::bitStreamSize - count);
216         t |= (p.bitStream & bitmask).convert_to<unsigned long long>();
217         p.bitStream >>= count;
218         p.bitCount -= count;
219         return 0;
220     }
221 };
222 
223 /** @brief Specialization of UnpackSingle for std::optional<T> */
224 template <typename T>
225 struct UnpackSingle<std::optional<T>>
226 {
227     static int op(Payload& p, std::optional<T>& t)
228     {
229         bool priorError = p.unpackError;
230         size_t priorIndex = p.rawIndex;
231         // more stuff to unroll if partial bytes are out
232         size_t priorBitCount = p.bitCount;
233         fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
234         t.emplace();
235         int ret = UnpackSingle<T>::op(p, *t);
236         if (ret != 0)
237         {
238             t.reset();
239             p.rawIndex = priorIndex;
240             p.bitStream = priorBits;
241             p.bitCount = priorBitCount;
242             p.unpackError = priorError;
243         }
244         return 0;
245     }
246 };
247 
248 /** @brief Specialization of UnpackSingle for std::array<T, N> */
249 template <typename T, size_t N>
250 struct UnpackSingle<std::array<T, N>>
251 {
252     static int op(Payload& p, std::array<T, N>& t)
253     {
254         int ret = 0;
255         size_t priorIndex = p.rawIndex;
256         for (auto& v : t)
257         {
258             ret = UnpackSingle<T>::op(p, v);
259             if (ret)
260             {
261                 p.rawIndex = priorIndex;
262                 t = std::array<T, N>();
263                 break;
264             }
265         }
266         return ret;
267     }
268 };
269 
270 /** @brief Specialization of UnpackSingle for std::array<uint8_t> */
271 template <size_t N>
272 struct UnpackSingle<std::array<uint8_t, N>>
273 {
274     static int op(Payload& p, std::array<uint8_t, N>& t)
275     {
276         if (p.raw.size() - p.rawIndex < N)
277         {
278             t.fill(0);
279             return -1;
280         }
281         // copy out the bytes
282         std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N,
283                   t.begin());
284         p.rawIndex += N;
285         return 0;
286     }
287 };
288 
289 /** @brief Specialization of UnpackSingle for std::vector<T> */
290 template <typename T>
291 struct UnpackSingle<std::vector<T>>
292 {
293     static int op(Payload& p, std::vector<T>& t)
294     {
295         while (p.rawIndex < p.raw.size())
296         {
297             t.emplace_back();
298             if (UnpackSingle<T>::op(p, t.back()))
299             {
300                 t.pop_back();
301                 break;
302             }
303         }
304         // unpacking a vector is always successful:
305         // either stuff was unpacked successfully (return 0)
306         // or stuff was not unpacked, but should still return
307         // success because an empty vector or a not-fully-unpacked
308         // payload is not a failure.
309         return 0;
310     }
311 };
312 
313 /** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
314 template <>
315 struct UnpackSingle<std::vector<uint8_t>>
316 {
317     static int op(Payload& p, std::vector<uint8_t>& t)
318     {
319         // copy out the remainder of the message
320         t.reserve(p.raw.size() - p.rawIndex);
321         t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
322         p.rawIndex = p.raw.size();
323         return 0;
324     }
325 };
326 
327 /** @brief Specialization of UnpackSingle for Payload */
328 template <>
329 struct UnpackSingle<Payload>
330 {
331     static int op(Payload& p, Payload& t)
332     {
333         t = p;
334         // mark that this payload is being included in the args
335         p.trailingOk = true;
336         return 0;
337     }
338 };
339 
340 } // namespace details
341 
342 } // namespace message
343 
344 } // namespace ipmi
345