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