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
103         {
104             if constexpr (utility::is_tuple<T>::value)
105             {
106                 bool priorError = p.unpackError;
107                 size_t priorIndex = p.rawIndex;
108                 // more stuff to unroll if partial bytes are out
109                 size_t priorBitCount = p.bitCount;
110                 fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
111                 int ret = p.unpack(t);
112                 if (ret != 0)
113                 {
114                     t = T();
115                     p.rawIndex = priorIndex;
116                     p.bitStream = priorBits;
117                     p.bitCount = priorBitCount;
118                     p.unpackError = priorError;
119                 }
120                 return 0;
121             }
122         }
123     }
124 };
125 
126 /** @struct UnpackSingle
127  *  @brief Utility to unpack a single C++ element from a Payload
128  *
129  *  Specialization to unpack std::string represented as a
130  *  UCSD-Pascal style string
131  */
132 template <>
133 struct UnpackSingle<std::string>
134 {
135     static int op(Payload& p, std::string& t)
136     {
137         // pop len first
138         if (p.rawIndex > (p.raw.size() - sizeof(uint8_t)))
139         {
140             return 1;
141         }
142         uint8_t len = p.raw[p.rawIndex++];
143         // check to see that there are n bytes left
144         auto [first, last] = p.pop<char>(len);
145         if (first == last)
146         {
147             return 1;
148         }
149         t.reserve(last - first);
150         t.insert(0, first, (last - first));
151         return 0;
152     }
153 };
154 
155 /** @brief Specialization of UnpackSingle for fixed_uint_t types
156  */
157 template <unsigned N>
158 struct UnpackSingle<fixed_uint_t<N>>
159 {
160     static int op(Payload& p, fixed_uint_t<N>& t)
161     {
162         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
163         constexpr size_t count = N;
164         // acquire enough bits in the stream to fulfill the Payload
165         if (p.fillBits(count))
166         {
167             return -1;
168         }
169         fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
170         t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>();
171         p.bitStream >>= count;
172         p.bitCount -= count;
173         return 0;
174     }
175 };
176 
177 /** @brief Specialization of UnpackSingle for bool. */
178 template <>
179 struct UnpackSingle<bool>
180 {
181     static int op(Payload& p, bool& b)
182     {
183         // acquire enough bits in the stream to fulfill the Payload
184         if (p.fillBits(1))
185         {
186             return -1;
187         }
188         b = static_cast<bool>(p.bitStream & 0x01);
189         // clear bits from stream
190         p.bitStream >>= 1;
191         p.bitCount -= 1;
192         return 0;
193     }
194 };
195 
196 /** @brief Specialization of UnpackSingle for std::bitset<N>
197  */
198 template <size_t N>
199 struct UnpackSingle<std::bitset<N>>
200 {
201     static int op(Payload& p, std::bitset<N>& t)
202     {
203         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
204         size_t count = N;
205         // acquire enough bits in the stream to fulfill the Payload
206         if (p.fillBits(count))
207         {
208             return -1;
209         }
210         fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
211         t |= (p.bitStream & bitmask).convert_to<unsigned long long>();
212         p.bitStream >>= count;
213         p.bitCount -= count;
214         return 0;
215     }
216 };
217 
218 /** @brief Specialization of UnpackSingle for std::optional<T> */
219 template <typename T>
220 struct UnpackSingle<std::optional<T>>
221 {
222     static int op(Payload& p, std::optional<T>& t)
223     {
224         bool priorError = p.unpackError;
225         size_t priorIndex = p.rawIndex;
226         // more stuff to unroll if partial bytes are out
227         size_t priorBitCount = p.bitCount;
228         fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
229         t.emplace();
230         int ret = UnpackSingle<T>::op(p, *t);
231         if (ret != 0)
232         {
233             t.reset();
234             p.rawIndex = priorIndex;
235             p.bitStream = priorBits;
236             p.bitCount = priorBitCount;
237             p.unpackError = priorError;
238         }
239         return 0;
240     }
241 };
242 
243 /** @brief Specialization of UnpackSingle for std::array<T, N> */
244 template <typename T, size_t N>
245 struct UnpackSingle<std::array<T, N>>
246 {
247     static int op(Payload& p, std::array<T, N>& t)
248     {
249         int ret = 0;
250         size_t priorIndex = p.rawIndex;
251         for (auto& v : t)
252         {
253             ret = UnpackSingle<T>::op(p, v);
254             if (ret)
255             {
256                 p.rawIndex = priorIndex;
257                 t = std::array<T, N>();
258                 break;
259             }
260         }
261         return ret;
262     }
263 };
264 
265 /** @brief Specialization of UnpackSingle for std::array<uint8_t> */
266 template <size_t N>
267 struct UnpackSingle<std::array<uint8_t, N>>
268 {
269     static int op(Payload& p, std::array<uint8_t, N>& t)
270     {
271         if (p.raw.size() - p.rawIndex < N)
272         {
273             t.fill(0);
274             return -1;
275         }
276         // copy out the bytes
277         std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N,
278                   t.begin());
279         p.rawIndex += N;
280         return 0;
281     }
282 };
283 
284 /** @brief Specialization of UnpackSingle for std::vector<T> */
285 template <typename T>
286 struct UnpackSingle<std::vector<T>>
287 {
288     static int op(Payload& p, std::vector<T>& t)
289     {
290         int ret = 0;
291         while (p.rawIndex < p.raw.size())
292         {
293             t.emplace_back();
294             ret = UnpackSingle<T>::op(p, t.back());
295             if (ret)
296             {
297                 t.pop_back();
298                 break;
299             }
300         }
301         return ret;
302     }
303 };
304 
305 /** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
306 template <>
307 struct UnpackSingle<std::vector<uint8_t>>
308 {
309     static int op(Payload& p, std::vector<uint8_t>& t)
310     {
311         // copy out the remainder of the message
312         t.reserve(p.raw.size() - p.rawIndex);
313         t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
314         p.rawIndex = p.raw.size();
315         return 0;
316     }
317 };
318 
319 /** @brief Specialization of UnpackSingle for Payload */
320 template <>
321 struct UnpackSingle<Payload>
322 {
323     static int op(Payload& p, Payload& t)
324     {
325         // mark that this payload is being included in the args
326         p.trailingOk = true;
327         t = p;
328         // reset the unpacking flags so it can be properly checked
329         t.trailingOk = false;
330         t.unpackCheck = true;
331         t.unpackError = false;
332         return 0;
333     }
334 };
335 
336 } // namespace details
337 
338 } // namespace message
339 
340 } // namespace ipmi
341