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