blob: 94f80f13a0d18cfb8d25863266f6dd5f326dbf94 [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 }
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 */
132template <>
133struct 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 */
157template <unsigned N>
158struct 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. */
178template <>
179struct 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 */
198template <size_t N>
199struct 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 }
William A. Kennington III0d49e472019-04-08 20:27:26 -0700210 fixed_uint_t<details::bitStreamSize> bitmask =
211 ~fixed_uint_t<details::bitStreamSize>(0) >>
212 (details::bitStreamSize - count);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700213 t |= (p.bitStream & bitmask).convert_to<unsigned long long>();
214 p.bitStream >>= count;
215 p.bitCount -= count;
216 return 0;
217 }
218};
219
220/** @brief Specialization of UnpackSingle for std::optional<T> */
221template <typename T>
222struct UnpackSingle<std::optional<T>>
223{
224 static int op(Payload& p, std::optional<T>& t)
225 {
226 bool priorError = p.unpackError;
227 size_t priorIndex = p.rawIndex;
228 // more stuff to unroll if partial bytes are out
229 size_t priorBitCount = p.bitCount;
230 fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
231 t.emplace();
232 int ret = UnpackSingle<T>::op(p, *t);
233 if (ret != 0)
234 {
235 t.reset();
236 p.rawIndex = priorIndex;
237 p.bitStream = priorBits;
238 p.bitCount = priorBitCount;
239 p.unpackError = priorError;
240 }
241 return 0;
242 }
243};
244
245/** @brief Specialization of UnpackSingle for std::array<T, N> */
246template <typename T, size_t N>
247struct UnpackSingle<std::array<T, N>>
248{
249 static int op(Payload& p, std::array<T, N>& t)
250 {
251 int ret = 0;
252 size_t priorIndex = p.rawIndex;
253 for (auto& v : t)
254 {
255 ret = UnpackSingle<T>::op(p, v);
256 if (ret)
257 {
258 p.rawIndex = priorIndex;
259 t = std::array<T, N>();
260 break;
261 }
262 }
263 return ret;
264 }
265};
266
267/** @brief Specialization of UnpackSingle for std::array<uint8_t> */
268template <size_t N>
269struct UnpackSingle<std::array<uint8_t, N>>
270{
271 static int op(Payload& p, std::array<uint8_t, N>& t)
272 {
273 if (p.raw.size() - p.rawIndex < N)
274 {
275 t.fill(0);
276 return -1;
277 }
278 // copy out the bytes
279 std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N,
280 t.begin());
281 p.rawIndex += N;
282 return 0;
283 }
284};
285
286/** @brief Specialization of UnpackSingle for std::vector<T> */
287template <typename T>
288struct UnpackSingle<std::vector<T>>
289{
290 static int op(Payload& p, std::vector<T>& t)
291 {
292 int ret = 0;
293 while (p.rawIndex < p.raw.size())
294 {
295 t.emplace_back();
296 ret = UnpackSingle<T>::op(p, t.back());
297 if (ret)
298 {
299 t.pop_back();
300 break;
301 }
302 }
303 return ret;
304 }
305};
306
307/** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
308template <>
309struct UnpackSingle<std::vector<uint8_t>>
310{
311 static int op(Payload& p, std::vector<uint8_t>& t)
312 {
313 // copy out the remainder of the message
314 t.reserve(p.raw.size() - p.rawIndex);
315 t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
316 p.rawIndex = p.raw.size();
317 return 0;
318 }
319};
320
321/** @brief Specialization of UnpackSingle for Payload */
322template <>
323struct UnpackSingle<Payload>
324{
325 static int op(Payload& p, Payload& t)
326 {
327 // mark that this payload is being included in the args
328 p.trailingOk = true;
329 t = p;
330 // reset the unpacking flags so it can be properly checked
331 t.trailingOk = false;
332 t.unpackCheck = true;
333 t.unpackError = false;
334 return 0;
335 }
336};
337
338} // namespace details
339
340} // namespace message
341
342} // namespace ipmi