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