1 /**
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 <memory>
21 #include <phosphor-logging/log.hpp>
22 #include <tuple>
23 #include <utility>
24 #include <vector>
25 
26 namespace ipmi
27 {
28 
29 namespace message
30 {
31 
32 namespace details
33 {
34 
35 /**************************************
36  * ipmi return type helpers
37  **************************************/
38 
39 template <typename NumericType, size_t byteIndex = 0>
40 void PackBytes(uint8_t* pointer, const NumericType& i)
41 {
42     if constexpr (byteIndex < sizeof(NumericType))
43     {
44         *pointer = static_cast<uint8_t>(i >> (8 * byteIndex));
45         PackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
46     }
47 }
48 
49 template <typename NumericType, size_t byteIndex = 0>
50 void PackBytesUnaligned(Payload& p, const NumericType& i)
51 {
52     if constexpr (byteIndex < sizeof(NumericType))
53     {
54         p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex)));
55         PackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
56     }
57 }
58 
59 /** @struct PackSingle
60  *  @brief Utility to pack a single C++ element into a Payload
61  *
62  *  User-defined types are expected to specialize this template in order to
63  *  get their functionality.
64  *
65  *  @tparam S - Type of element to pack.
66  */
67 template <typename T>
68 struct PackSingle
69 {
70     /** @brief Do the operation to pack element.
71      *
72      *  @param[in] p - Payload to pack into.
73      *  @param[out] t - The reference to pack item into.
74      */
75     static int op(Payload& p, T& t)
76     {
77         // if not on a byte boundary, must pack values LSbit/LSByte first
78         if (p.bitCount)
79         {
80             PackBytesUnaligned<T>(p, t);
81         }
82         else
83         {
84             // copy in bits to vector....
85             p.raw.resize(p.raw.size() + sizeof(T));
86             uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T);
87             PackBytes<T>(out, t);
88         }
89         return 0;
90     }
91 };
92 
93 /** @brief Specialization of PackSingle for std::string
94  *  represented as a UCSD-Pascal style string
95  */
96 template <>
97 struct PackSingle<std::string>
98 {
99     static int op(Payload& p, std::string& t)
100     {
101         // check length first
102         uint8_t len;
103         if (t.length() > std::numeric_limits<decltype(len)>::max())
104         {
105             using namespace phosphor::logging;
106             log<level::ERR>("long string truncated on IPMI message pack");
107             return 1;
108         }
109         len = static_cast<uint8_t>(t.length());
110         PackSingle<uint8_t>::op(p, len);
111         p.append(t.c_str(), t.c_str() + t.length());
112         return 0;
113     }
114 };
115 
116 /** @brief Specialization of PackSingle for fixed_uint_t types
117  */
118 template <unsigned N>
119 struct PackSingle<fixed_uint_t<N>>
120 {
121     static int op(Payload& p, fixed_uint_t<N>& t)
122     {
123         size_t count = N;
124         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
125         uint64_t bits = t;
126         while (count > 0)
127         {
128             size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT));
129             p.appendBits(appendCount, static_cast<uint8_t>(bits));
130             bits >>= CHAR_BIT;
131             count -= appendCount;
132         }
133         return 0;
134     }
135 };
136 
137 /** @brief Specialization of PackSingle for bool. */
138 template <>
139 struct PackSingle<bool>
140 {
141     static int op(Payload& p, bool& b)
142     {
143         p.appendBits(1, b);
144         return 0;
145     }
146 };
147 
148 /** @brief Specialization of PackSingle for std::bitset<N> */
149 template <size_t N>
150 struct PackSingle<std::bitset<N>>
151 {
152     static int op(Payload& p, std::bitset<N>& t)
153     {
154         size_t count = N;
155         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
156         unsigned long long bits = t.to_ullong();
157         while (count > 0)
158         {
159             size_t appendCount = std::min(count, size_t(CHAR_BIT));
160             p.appendBits(appendCount, static_cast<uint8_t>(bits));
161             bits >>= CHAR_BIT;
162             count -= appendCount;
163         }
164         return 0;
165     }
166 };
167 
168 /** @brief Specialization of PackSingle for std::array<T, N> */
169 template <typename T, size_t N>
170 struct PackSingle<std::array<T, N>>
171 {
172     static int op(Payload& p, std::array<T, N>& t)
173     {
174         int ret = 0;
175         for (auto& v : t)
176         {
177             int ret = PackSingle<T>::op(p, v);
178             if (ret)
179             {
180                 break;
181             }
182         }
183         return ret;
184     }
185 };
186 
187 /** @brief Specialization of PackSingle for std::vector<T> */
188 template <typename T>
189 struct PackSingle<std::vector<T>>
190 {
191     static int op(Payload& p, std::vector<T>& t)
192     {
193         int ret = 0;
194         for (auto& v : t)
195         {
196             int ret = PackSingle<T>::op(p, v);
197             if (ret)
198             {
199                 break;
200             }
201         }
202         return ret;
203     }
204 };
205 
206 /** @brief Specialization of PackSingle for std::vector<uint8_t> */
207 template <>
208 struct PackSingle<std::vector<uint8_t>>
209 {
210     static int op(Payload& p, std::vector<uint8_t>& t)
211     {
212         p.raw.reserve(p.raw.size() + t.size());
213         p.raw.insert(p.raw.end(), t.begin(), t.end());
214         return 0;
215     }
216 };
217 
218 } // namespace details
219 
220 } // namespace message
221 
222 } // namespace ipmi
223