blob: d9ccba4fecc10e915a56cce319791f0f2ec8aeb6 [file] [log] [blame]
Vernon Mauerye7329c72018-10-08 12:05:16 -07001/**
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
25namespace ipmi
26{
27
28namespace message
29{
30
31namespace details
32{
33
34/**************************************
35 * ipmi return type helpers
36 **************************************/
37
38template <typename NumericType, size_t byteIndex = 0>
39void 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
48template <typename NumericType, size_t byteIndex = 0>
49void 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 */
67template <typename T>
68struct 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 }
Vernon Mauerya3dd7662019-05-30 15:20:52 -0700102 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 }
Vernon Mauerycaabc362019-07-23 16:49:34 -0700118 return ret;
Vernon Mauerya3dd7662019-05-30 15:20:52 -0700119 }
Vernon Mauerye7329c72018-10-08 12:05:16 -0700120 else
121 {
Vernon Mauerya3dd7662019-05-30 15:20:52 -0700122 static_assert(
123 utility::dependent_false<T>::value,
124 "Attempt to unpack a type that has no IPMI unpack operation");
Vernon Mauerye7329c72018-10-08 12:05:16 -0700125 }
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 */
135template <>
136struct 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 */
160template <unsigned N>
161struct 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. */
181template <>
182struct 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 */
201template <size_t N>
202struct 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 }
William A. Kennington III0d49e472019-04-08 20:27:26 -0700213 fixed_uint_t<details::bitStreamSize> bitmask =
214 ~fixed_uint_t<details::bitStreamSize>(0) >>
215 (details::bitStreamSize - count);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700216 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> */
224template <typename T>
225struct 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> */
249template <typename T, size_t N>
250struct 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> */
271template <size_t N>
272struct 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> */
290template <typename T>
291struct UnpackSingle<std::vector<T>>
292{
293 static int op(Payload& p, std::vector<T>& t)
294 {
Vernon Mauerye7329c72018-10-08 12:05:16 -0700295 while (p.rawIndex < p.raw.size())
296 {
297 t.emplace_back();
Vernon Mauerycaabc362019-07-23 16:49:34 -0700298 if (UnpackSingle<T>::op(p, t.back()))
Vernon Mauerye7329c72018-10-08 12:05:16 -0700299 {
300 t.pop_back();
301 break;
302 }
303 }
Vernon Mauerycaabc362019-07-23 16:49:34 -0700304 // 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;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700310 }
311};
312
313/** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
314template <>
315struct 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 */
328template <>
329struct UnpackSingle<Payload>
330{
331 static int op(Payload& p, Payload& t)
332 {
William A. Kennington III51694c22019-04-24 01:44:44 -0700333 t = p;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700334 // mark that this payload is being included in the args
335 p.trailingOk = true;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700336 return 0;
337 }
338};
339
340} // namespace details
341
342} // namespace message
343
344} // namespace ipmi