blob: 5eb8054fe6fe2e2cd6a3ffb908df2fc5369fb5fd [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
Vernon Mauerye7329c72018-10-08 12:05:16 -070018#include <ipmid/message/types.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050019
20#include <array>
Vernon Mauerye7329c72018-10-08 12:05:16 -070021#include <optional>
22#include <string>
23#include <tuple>
24#include <vector>
25
26namespace ipmi
27{
28
29namespace message
30{
31
32namespace details
33{
34
35/**************************************
36 * ipmi return type helpers
37 **************************************/
38
39template <typename NumericType, size_t byteIndex = 0>
40void 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
49template <typename NumericType, size_t byteIndex = 0>
50void 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 */
68template <typename T>
69struct 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 }
Vernon Mauerya3dd7662019-05-30 15:20:52 -0700103 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 }
Vernon Mauerycaabc362019-07-23 16:49:34 -0700119 return ret;
Vernon Mauerya3dd7662019-05-30 15:20:52 -0700120 }
Vernon Mauerye7329c72018-10-08 12:05:16 -0700121 else
122 {
Vernon Mauerya3dd7662019-05-30 15:20:52 -0700123 static_assert(
124 utility::dependent_false<T>::value,
125 "Attempt to unpack a type that has no IPMI unpack operation");
Vernon Mauerye7329c72018-10-08 12:05:16 -0700126 }
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 */
136template <>
137struct 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 */
Tim Leeb4905912022-06-18 02:51:13 +0800161template <bitcount_t N>
Vernon Mauerye7329c72018-10-08 12:05:16 -0700162struct 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. */
182template <>
183struct 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 */
202template <size_t N>
203struct 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 }
William A. Kennington III0d49e472019-04-08 20:27:26 -0700214 fixed_uint_t<details::bitStreamSize> bitmask =
215 ~fixed_uint_t<details::bitStreamSize>(0) >>
216 (details::bitStreamSize - count);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700217 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> */
225template <typename T>
226struct 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;
Jonathan Doman202702b2023-09-15 16:21:22 -0700235 T value;
236 int ret = UnpackSingle<T>::op(p, value);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700237 if (ret != 0)
238 {
239 t.reset();
240 p.rawIndex = priorIndex;
241 p.bitStream = priorBits;
242 p.bitCount = priorBitCount;
243 p.unpackError = priorError;
244 }
Jonathan Doman202702b2023-09-15 16:21:22 -0700245 else
246 {
247 t.emplace(std::move(value));
248 }
Vernon Mauerye7329c72018-10-08 12:05:16 -0700249 return 0;
250 }
251};
252
253/** @brief Specialization of UnpackSingle for std::array<T, N> */
254template <typename T, size_t N>
255struct 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> */
276template <size_t N>
277struct 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> */
295template <typename T>
296struct UnpackSingle<std::vector<T>>
297{
298 static int op(Payload& p, std::vector<T>& t)
299 {
Vernon Mauerye7329c72018-10-08 12:05:16 -0700300 while (p.rawIndex < p.raw.size())
301 {
302 t.emplace_back();
Vernon Mauerycaabc362019-07-23 16:49:34 -0700303 if (UnpackSingle<T>::op(p, t.back()))
Vernon Mauerye7329c72018-10-08 12:05:16 -0700304 {
305 t.pop_back();
306 break;
307 }
308 }
Vernon Mauerycaabc362019-07-23 16:49:34 -0700309 // 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;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700315 }
316};
317
318/** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
319template <>
320struct 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
Vernon Mauery997952a2021-07-30 14:06:14 -0700332/** @brief Specialization of UnpackSingle for SecureBuffer */
333template <>
334struct 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
Vernon Mauerye7329c72018-10-08 12:05:16 -0700346/** @brief Specialization of UnpackSingle for Payload */
347template <>
348struct UnpackSingle<Payload>
349{
350 static int op(Payload& p, Payload& t)
351 {
William A. Kennington III51694c22019-04-24 01:44:44 -0700352 t = p;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700353 // mark that this payload is being included in the args
354 p.trailingOk = true;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700355 return 0;
356 }
357};
358
359} // namespace details
360
361} // namespace message
362
363} // namespace ipmi