1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LOADER_JSON_UTIL_H
17 #define LOADER_JSON_UTIL_H
18 
19 #include <cerrno>
20 #include <cstdlib>
21 
22 #include <base/containers/array_view.h>
23 #include <base/containers/fixed_string.h>
24 #include <base/containers/string.h>
25 #include <base/containers/string_view.h>
26 #include <base/containers/vector.h>
27 #include <base/math/mathf.h>
28 #include <base/math/vector.h>
29 #include <base/util/uid.h>
30 #include <base/util/uid_util.h>
31 #include <core/json/json.h>
32 #include <core/namespace.h>
33 
34 #include "util/log.h"
35 #include "util/string_util.h"
36 
RENDER_BEGIN_NAMESPACE()37 RENDER_BEGIN_NAMESPACE()
38 #define CORE_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                                                    \
39     template<typename BasicJsonType>                                                                                \
40     inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                                       \
41     {                                                                                                               \
42         static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");                              \
43         static constexpr std::pair<ENUM_TYPE, BASE_NS::string_view> m[] = __VA_ARGS__;                              \
44         auto it = std::find_if(std::begin(m), std::end(m),                                                          \
45             [e](const std::pair<ENUM_TYPE, BASE_NS::string_view>& ej_pair) -> bool { return ej_pair.first == e; }); \
46         j = ((it != std::end(m)) ? it : std::begin(m))->second;                                                     \
47     }                                                                                                               \
48     template<typename BasicJsonType>                                                                                \
49     inline bool FromJson(const BasicJsonType& j, ENUM_TYPE& e)                                                      \
50     {                                                                                                               \
51         static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");                              \
52         static constexpr std::pair<ENUM_TYPE, BASE_NS::string_view> m[] = __VA_ARGS__;                              \
53         if (j.is_string()) {                                                                                        \
54             auto it = std::find_if(std::begin(m), std::end(m),                                                      \
55                 [name = j.string_](const std::pair<ENUM_TYPE, BASE_NS::string_view>& ej_pair) -> bool {             \
56                     return ej_pair.second == name;                                                                  \
57                 });                                                                                                 \
58             e = ((it != std::end(m)) ? it : std::begin(m))->first;                                                  \
59             return true;                                                                                            \
60         }                                                                                                           \
61         return false;                                                                                               \
62     }
63 
64 template<typename T, BASE_NS::enable_if_t<BASE_NS::is_same_v<bool, T>, bool> = true>
65 inline bool FromJson(const CORE_NS::json::value& jsonData, T& result)
66 {
67     if (jsonData.is_boolean()) {
68         result = static_cast<T>(jsonData.boolean_);
69         return true;
70     }
71     return false;
72 }
73 
74 template<typename T, BASE_NS::enable_if_t<!BASE_NS::is_same_v<bool, T> && BASE_NS::is_arithmetic_v<T>, bool> = true>
FromJson(const CORE_NS::json::value & jsonData,T & result)75 inline bool FromJson(const CORE_NS::json::value& jsonData, T& result)
76 {
77     if (jsonData.is_number()) {
78         result = jsonData.as_number<T>();
79         return true;
80     }
81     return false;
82 }
83 
84 template<typename T, BASE_NS::enable_if_t<BASE_NS::is_convertible_v<T, BASE_NS::string_view>, bool> = true>
FromJson(const CORE_NS::json::value & jsonData,T & result)85 inline bool FromJson(const CORE_NS::json::value& jsonData, T& result)
86 {
87     if (jsonData.is_string()) {
88         result = BASE_NS::string_view { jsonData.string_ };
89         return true;
90     }
91     return false;
92 }
93 
94 namespace Detail {
95 constexpr const BASE_NS::string_view INVALID_DATATYPE = "Failed to read value, invalid datatype: ";
96 template<typename T>
Convert(const CORE_NS::json::value & value)97 inline T Convert(const CORE_NS::json::value& value)
98 {
99     T result;
100     FromJson(value, result);
101     return result;
102 }
103 
104 template<typename Container, typename OutIt, typename Fn>
Transform(Container && container,OutIt dest,Fn func)105 inline OutIt Transform(Container&& container, OutIt dest, Fn func)
106 {
107     return std::transform(container.begin(), container.end(), dest, func);
108 }
109 } // namespace Detail
110 
111 template<class T>
112 struct JsonContext {
113     T data;
114     BASE_NS::string error;
115 };
116 
ParseHex(BASE_NS::string_view str,uint32_t & val)117 inline bool ParseHex(BASE_NS::string_view str, uint32_t& val)
118 {
119     errno = 0;
120     char* end;
121     constexpr const int hexadecimalBase = 16;
122     val = std::strtoul(str.data(), &end, hexadecimalBase);
123     return !(end != (str.end().ptr()) || errno != 0);
124 }
125 
126 template<class JsonType, typename T>
FromJson(const JsonType & jsonData,BASE_NS::array_view<T> container)127 inline void FromJson(const JsonType& jsonData, BASE_NS::array_view<T> container)
128 {
129     if (jsonData.is_array()) {
130         const auto view =
131             BASE_NS::array_view(jsonData.array_.data(), BASE_NS::Math::min(jsonData.array_.size(), container.size()));
132         Detail::Transform(view, std::begin(container), [](const JsonType& value) { return Detail::Convert<T>(value); });
133     }
134 }
135 
136 template<class JsonType, typename T>
FromJson(const JsonType & jsonData,BASE_NS::vector<T> & container)137 inline void FromJson(const JsonType& jsonData, BASE_NS::vector<T>& container)
138 {
139     if (jsonData.is_array()) {
140         Detail::Transform(jsonData.array_, std::back_inserter(container),
141             [](const JsonType& value) { return Detail::Convert<T>(value); });
142     }
143 }
144 
145 template<class JsonType, typename T, size_t N>
FromJson(const JsonType & jsonData,T (& container)[N])146 inline void FromJson(const JsonType& jsonData, T (&container)[N])
147 {
148     FromJson(jsonData, BASE_NS::array_view(container));
149 }
150 
151 template<class JsonType, typename T, typename = BASE_NS::enable_if_t<BASE_NS::is_arithmetic_v<T>>>
SafeGetJsonValue(const JsonType & jsonData,const BASE_NS::string_view element,BASE_NS::string & error,T & output)152 bool SafeGetJsonValue(const JsonType& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, T& output)
153 {
154     if (auto const pos = jsonData.find(element); pos) {
155         if (!FromJson(*pos, output)) {
156             error += element + ": expected number.\n";
157             return false;
158         }
159     }
160     return true;
161 }
162 
163 template<class JsonType, class T, BASE_NS::enable_if_t<BASE_NS::is_convertible_v<T, BASE_NS::string_view>, bool> = true>
SafeGetJsonValue(const JsonType & jsonData,const BASE_NS::string_view element,BASE_NS::string & error,T & output)164 bool SafeGetJsonValue(const JsonType& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, T& output)
165 {
166     if (auto const pos = jsonData.find(element); pos) {
167         if (!FromJson(*pos, output)) {
168             error += element + ": expected string.\n";
169             return false;
170         }
171     }
172     return true;
173 }
174 
175 template<class JsonType, class T>
SafeGetJsonEnum(const JsonType & jsonData,const BASE_NS::string_view element,BASE_NS::string & error,T & output)176 bool SafeGetJsonEnum(const JsonType& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, T& output)
177 {
178     if (auto const pos = jsonData.find(element); pos) {
179         if (!FromJson(*pos, output)) {
180             error += Detail::INVALID_DATATYPE + element + " (" + CORE_NS::json::to_string(*pos) + ")\n";
181             return false;
182         }
183     }
184     return true;
185 }
186 
187 template<class T, class JsonType>
SafeGetJsonBitfield(const JsonType & jData,const BASE_NS::string_view element,BASE_NS::string & error,uint32_t & output)188 bool SafeGetJsonBitfield(
189     const JsonType& jData, const BASE_NS::string_view element, BASE_NS::string& error, uint32_t& output)
190 {
191     if (auto const pos = jData.find(element); pos) {
192         output = 0;
193 
194         if (pos->is_string()) {
195             for (const auto& field : StringUtil::Split(pos->string_, BASE_NS::string_view("|"))) {
196                 if (const T value = Detail::Convert<T>(field); value != static_cast<T>(0x7FFFFFFF)) {
197                     output |= value;
198                 } else {
199                     PLUGIN_LOG_W("Unknown bit value in \'%s\' \'%s\'", element.data(), BASE_NS::string(field).data());
200                 }
201             }
202         } else {
203             error += Detail::INVALID_DATATYPE + element + " (" + CORE_NS::json::to_string(*pos) + ")\n";
204             return false;
205         }
206     }
207     return true;
208 }
209 
210 template<class JsonType>
SafeGetJsonUidValue(const JsonType & jsonData,const BASE_NS::string_view element,BASE_NS::string & error,BASE_NS::Uid & output)211 bool SafeGetJsonUidValue(
212     const JsonType& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, BASE_NS::Uid& output)
213 {
214     if (auto const pos = jsonData.find(element); pos) {
215         output = {};
216         if (pos->is_string()) {
217             constexpr size_t uidLength { to_string(BASE_NS::Uid {}).size() }; // default uid length
218             if (pos->string_.size() == uidLength) {
219                 output = StringToUid(pos->string_);
220                 return true;
221             } else {
222                 error += element + ": size does not match uid.\n";
223             }
224         } else {
225             error += element + ": expected string as uid.\n";
226         }
227     }
228     return false;
229 }
230 
231 template<class ArrayType, class JsonType, class ResultType>
ParseArray(JsonType const & jData,char const * element,BASE_NS::vector<ArrayType> & out,ResultType & res)232 void ParseArray(JsonType const& jData, char const* element, BASE_NS::vector<ArrayType>& out, ResultType& res)
233 {
234     if (auto const array = jData.find(element); array && array->is_array()) {
235         out.reserve(out.size() + array->array_.size());
236         Detail::Transform(array->array_, std::back_inserter(out), [&res](const JsonType& value) {
237             JsonContext<ArrayType> result;
238             FromJson(value, result);
239             if (!result.error.empty()) {
240                 res.error += result.error;
241             }
242             return result.data;
243         });
244     }
245 }
246 
247 template<class JsonType>
SafeGetJsonMask(const JsonType & jsonData,const BASE_NS::string_view element,BASE_NS::string & error,uint32_t & output)248 void SafeGetJsonMask(
249     const JsonType& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, uint32_t& output)
250 {
251     if (const auto mask = jsonData.find(element); mask) {
252         if (mask->is_string() && ParseHex(mask->string_, output)) {
253         } else if (mask->is_number()) {
254             output = mask->template as_number<uint32_t>();
255         } else {
256             error += "Failed to read value: " + element + " (" + CORE_NS::json::to_string(*mask) + ")";
257         }
258     }
259 }
260 
261 template<class JsonType, typename T,
262     BASE_NS::enable_if_t<BASE_NS::is_array_v<decltype(T::data)> &&
263                              BASE_NS::is_arithmetic_v<BASE_NS::remove_extent_t<decltype(T::data)>>,
264         bool> = true>
FromJson(const JsonType & jsonData,T & output)265 inline void FromJson(const JsonType& jsonData, T& output)
266 {
267     FromJson(jsonData, output.data);
268 }
269 RENDER_END_NAMESPACE()
270 
271 #endif // LOADER_JSON_UTIL_H
272