blob: d96928fb561b658b8256cffe977f5759cdfa33b7 [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 }
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> */
219template <typename T>
220struct 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> */
244template <typename T, size_t N>
245struct 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> */
266template <size_t N>
267struct 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> */
285template <typename T>
286struct 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> */
306template <>
307struct 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 */
320template <>
321struct 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