ComplexArgument.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*
  2. * Copyright (c) 2022 Project CHIP Authors
  3. * All rights reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. /**
  19. * This file allocate/free memory using the chip platform abstractions
  20. * (Platform::MemoryCalloc and Platform::MemoryFree) for hosting a subset of the
  21. * data model internal types until they are consumed by the DataModel::Encode machinery:
  22. * - chip::app:DataModel::List<T>
  23. * - chip::ByteSpan
  24. * - chip::CharSpan
  25. *
  26. * Memory allocation happens during the 'Setup' phase, while memory deallocation happens
  27. * during the 'Finalize' phase.
  28. *
  29. * The 'Finalize' phase during the destructor phase, and if needed, 'Finalize' will call
  30. * the 'Finalize' phase of its descendant.
  31. */
  32. #pragma once
  33. #include <app-common/zap-generated/cluster-objects.h>
  34. #include <app/data-model/List.h>
  35. #include <app/data-model/Nullable.h>
  36. #include <commands/common/HexConversion.h>
  37. #include <json/json.h>
  38. #include <lib/core/Optional.h>
  39. #include <lib/support/BytesToHex.h>
  40. #include <lib/support/CHIPMemString.h>
  41. #include <lib/support/SafeInt.h>
  42. #include "JsonParser.h"
  43. inline constexpr uint8_t kMaxLabelLength = UINT8_MAX;
  44. inline constexpr const char kNullString[] = "null";
  45. class ComplexArgumentParser
  46. {
  47. public:
  48. ComplexArgumentParser() {}
  49. template <typename T,
  50. typename std::enable_if_t<std::is_integral<T>::value && !std::is_signed<T>::value &&
  51. !std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, bool>::value,
  52. int> = 0>
  53. static CHIP_ERROR Setup(const char * label, T & request, Json::Value value)
  54. {
  55. if (value.isNumeric())
  56. {
  57. if (chip::CanCastTo<T>(value.asLargestUInt()))
  58. {
  59. request = static_cast<T>(value.asLargestUInt());
  60. return CHIP_NO_ERROR;
  61. }
  62. }
  63. else if (value.isString())
  64. {
  65. // Check for a hex number; JSON does not support those as numbers,
  66. // so they have to be done as strings. And we might as well support
  67. // string-encoded unsigned numbers in general if we're doing that.
  68. bool isHexNotation = strncmp(value.asCString(), "0x", 2) == 0 || strncmp(value.asCString(), "0X", 2) == 0;
  69. std::stringstream str;
  70. isHexNotation ? str << std::hex << value.asCString() : str << value.asCString();
  71. uint64_t val;
  72. str >> val;
  73. if (!str.fail() && str.eof() && chip::CanCastTo<T>(val))
  74. {
  75. request = static_cast<T>(val);
  76. return CHIP_NO_ERROR;
  77. }
  78. }
  79. ChipLogError(chipTool, "Error while encoding %s as an unsigned integer.", label);
  80. return CHIP_ERROR_INVALID_ARGUMENT;
  81. }
  82. template <typename T, std::enable_if_t<std::is_signed<T>::value, bool> = true>
  83. static CHIP_ERROR Setup(const char * label, T & request, Json::Value value)
  84. {
  85. if (!value.isNumeric() || !chip::CanCastTo<T>(value.asLargestInt()))
  86. {
  87. ChipLogError(chipTool, "Error while encoding %s as an unsigned integer.", label);
  88. return CHIP_ERROR_INVALID_ARGUMENT;
  89. }
  90. request = static_cast<T>(value.asLargestInt());
  91. return CHIP_NO_ERROR;
  92. }
  93. template <typename T, typename std::enable_if_t<std::is_enum<T>::value, int> = 0>
  94. static CHIP_ERROR Setup(const char * label, T & request, Json::Value value)
  95. {
  96. std::underlying_type_t<T> requestValue;
  97. ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
  98. request = static_cast<T>(requestValue);
  99. return CHIP_NO_ERROR;
  100. }
  101. template <typename T>
  102. static CHIP_ERROR Setup(const char * label, chip::BitFlags<T> & request, Json::Value & value)
  103. {
  104. T requestValue;
  105. ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
  106. request = chip::BitFlags<T>(requestValue);
  107. return CHIP_NO_ERROR;
  108. }
  109. template <typename T>
  110. static CHIP_ERROR Setup(const char * label, chip::BitMask<T> & request, Json::Value & value)
  111. {
  112. T requestValue;
  113. ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
  114. request = chip::BitMask<T>(requestValue);
  115. return CHIP_NO_ERROR;
  116. }
  117. template <typename T>
  118. static CHIP_ERROR Setup(const char * label, chip::Optional<T> & request, Json::Value & value)
  119. {
  120. T requestValue;
  121. ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
  122. request = chip::Optional<T>(requestValue);
  123. return CHIP_NO_ERROR;
  124. }
  125. template <typename T>
  126. static CHIP_ERROR Setup(const char * label, chip::app::DataModel::Nullable<T> & request, Json::Value & value)
  127. {
  128. if (value.isNull())
  129. {
  130. request.SetNull();
  131. return CHIP_NO_ERROR;
  132. }
  133. T requestValue;
  134. ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
  135. request = chip::app::DataModel::Nullable<T>(requestValue);
  136. return CHIP_NO_ERROR;
  137. }
  138. template <typename T>
  139. static CHIP_ERROR Setup(const char * label, chip::app::DataModel::List<T> & request, Json::Value & value)
  140. {
  141. if (!value.isArray())
  142. {
  143. ChipLogError(chipTool, "Error while encoding %s as an array.", label);
  144. return CHIP_ERROR_INVALID_ARGUMENT;
  145. }
  146. auto content = static_cast<typename std::remove_const<T>::type *>(chip::Platform::MemoryCalloc(value.size(), sizeof(T)));
  147. VerifyOrReturnError(content != nullptr, CHIP_ERROR_NO_MEMORY);
  148. Json::ArrayIndex size = value.size();
  149. for (Json::ArrayIndex i = 0; i < size; i++)
  150. {
  151. char labelWithIndex[kMaxLabelLength];
  152. // GCC 7.0.1 has introduced some new warnings for snprintf (-Werror=format-truncation) by default.
  153. // This is not particularly useful when using snprintf and especially in this context, so in order
  154. // to disable the warning the %s is constrained to be of max length: (254 - 11 - 2) where:
  155. // - 254 is kMaxLabelLength - 1 (for null)
  156. // - 11 is the maximum length of a %d (-2147483648, 2147483647)
  157. // - 2 is the length for the "[" and "]" characters.
  158. snprintf(labelWithIndex, sizeof(labelWithIndex), "%.241s[%d]", label, i);
  159. ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithIndex, content[i], value[i]));
  160. }
  161. request = chip::app::DataModel::List<T>(content, value.size());
  162. return CHIP_NO_ERROR;
  163. }
  164. static CHIP_ERROR Setup(const char * label, chip::ByteSpan & request, Json::Value & value)
  165. {
  166. if (!value.isString())
  167. {
  168. ChipLogError(chipTool, "Error while encoding %s as an octet string: Not a string.", label);
  169. return CHIP_ERROR_INVALID_ARGUMENT;
  170. }
  171. auto str = value.asString();
  172. auto size = str.size();
  173. uint8_t * buffer = nullptr;
  174. if (IsStrString(str.c_str()))
  175. {
  176. // Skip the prefix
  177. str.erase(0, kStrStringPrefixLen);
  178. size = str.size();
  179. buffer = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(size, sizeof(uint8_t)));
  180. VerifyOrReturnError(buffer != nullptr, CHIP_ERROR_NO_MEMORY);
  181. memcpy(buffer, str.c_str(), size);
  182. }
  183. else
  184. {
  185. if (IsHexString(str.c_str()))
  186. {
  187. // Skip the prefix
  188. str.erase(0, kHexStringPrefixLen);
  189. size = str.size();
  190. }
  191. CHIP_ERROR err = HexToBytes(
  192. chip::CharSpan(str.c_str(), size),
  193. [&buffer](size_t allocSize) {
  194. buffer = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(allocSize, sizeof(uint8_t)));
  195. return buffer;
  196. },
  197. &size);
  198. if (err != CHIP_NO_ERROR)
  199. {
  200. if (buffer != nullptr)
  201. {
  202. chip::Platform::MemoryFree(buffer);
  203. }
  204. return err;
  205. }
  206. }
  207. request = chip::ByteSpan(buffer, size);
  208. return CHIP_NO_ERROR;
  209. }
  210. static CHIP_ERROR Setup(const char * label, chip::CharSpan & request, Json::Value & value)
  211. {
  212. if (!value.isString())
  213. {
  214. ChipLogError(chipTool, "Error while encoding %s as a string: Not a string.", label);
  215. return CHIP_ERROR_INVALID_ARGUMENT;
  216. }
  217. size_t size = strlen(value.asCString());
  218. auto buffer = static_cast<char *>(chip::Platform::MemoryCalloc(size, sizeof(char)));
  219. VerifyOrReturnError(buffer != nullptr, CHIP_ERROR_NO_MEMORY);
  220. memcpy(buffer, value.asCString(), size);
  221. request = chip::CharSpan(buffer, size);
  222. return CHIP_NO_ERROR;
  223. }
  224. static CHIP_ERROR Setup(const char * label, float & request, Json::Value & value)
  225. {
  226. if (!value.isNumeric())
  227. {
  228. ChipLogError(chipTool, "Error while encoding %s as a float: Not a number.", label);
  229. return CHIP_ERROR_INVALID_ARGUMENT;
  230. }
  231. request = static_cast<float>(value.asFloat());
  232. return CHIP_NO_ERROR;
  233. }
  234. static CHIP_ERROR Setup(const char * label, double & request, Json::Value & value)
  235. {
  236. if (!value.isNumeric())
  237. {
  238. ChipLogError(chipTool, "Error while encoding %s as a double: Not a number.", label);
  239. return CHIP_ERROR_INVALID_ARGUMENT;
  240. }
  241. request = static_cast<double>(value.asDouble());
  242. return CHIP_NO_ERROR;
  243. }
  244. static CHIP_ERROR Setup(const char * label, bool & request, Json::Value & value)
  245. {
  246. if (!value.isBool())
  247. {
  248. ChipLogError(chipTool, "Error while encoding %s as a boolean: Not a boolean.", label);
  249. return CHIP_ERROR_INVALID_ARGUMENT;
  250. }
  251. request = value.asBool();
  252. return CHIP_NO_ERROR;
  253. }
  254. static CHIP_ERROR EnsureMemberExist(const char * label, const char * memberName, bool hasMember)
  255. {
  256. if (hasMember)
  257. {
  258. return CHIP_NO_ERROR;
  259. }
  260. ChipLogError(chipTool, "%s is required. Should be provided as {\"%s\": value}", label, memberName);
  261. return CHIP_ERROR_INVALID_ARGUMENT;
  262. }
  263. static CHIP_ERROR EnsureNoMembersRemaining(const char * label, const Json::Value & value)
  264. {
  265. auto remainingFields = value.getMemberNames();
  266. if (remainingFields.size() == 0)
  267. {
  268. return CHIP_NO_ERROR;
  269. }
  270. #if CHIP_ERROR_LOGGING
  271. for (auto & field : remainingFields)
  272. {
  273. ChipLogError(chipTool, "Unexpected field name: '%s.%s'", label, field.c_str());
  274. }
  275. #endif // CHIP_ERROR_LOGGING
  276. return CHIP_ERROR_INVALID_ARGUMENT;
  277. }
  278. template <typename T>
  279. static void Finalize(T & request)
  280. {
  281. // Nothing to do
  282. }
  283. template <typename T>
  284. static void Finalize(chip::Optional<T> & request)
  285. {
  286. VerifyOrReturn(request.HasValue());
  287. ComplexArgumentParser::Finalize(request.Value());
  288. }
  289. template <typename T>
  290. static void Finalize(chip::app::DataModel::Nullable<T> & request)
  291. {
  292. VerifyOrReturn(!request.IsNull());
  293. ComplexArgumentParser::Finalize(request.Value());
  294. }
  295. static void Finalize(chip::ByteSpan & request)
  296. {
  297. VerifyOrReturn(request.data() != nullptr);
  298. chip::Platform::MemoryFree(reinterpret_cast<void *>(const_cast<uint8_t *>(request.data())));
  299. }
  300. static void Finalize(chip::CharSpan & request)
  301. {
  302. VerifyOrReturn(request.data() != nullptr);
  303. chip::Platform::MemoryFree(reinterpret_cast<void *>(const_cast<char *>(request.data())));
  304. }
  305. template <typename T>
  306. static void Finalize(chip::app::DataModel::List<T> & request)
  307. {
  308. VerifyOrReturn(request.data() != nullptr);
  309. size_t size = request.size();
  310. auto data = const_cast<typename std::remove_const<T>::type *>(request.data());
  311. for (size_t i = 0; i < size; i++)
  312. {
  313. Finalize(data[i]);
  314. }
  315. chip::Platform::MemoryFree(reinterpret_cast<void *>(data));
  316. }
  317. #include <zap-generated/cluster/ComplexArgumentParser.h>
  318. };
  319. class ComplexArgument
  320. {
  321. public:
  322. virtual ~ComplexArgument() {}
  323. virtual CHIP_ERROR Parse(const char * label, const char * json) = 0;
  324. virtual void Reset() = 0;
  325. };
  326. template <typename T>
  327. class TypedComplexArgument : public ComplexArgument
  328. {
  329. public:
  330. TypedComplexArgument() {}
  331. TypedComplexArgument(T * request) : mRequest(request) {}
  332. ~TypedComplexArgument()
  333. {
  334. if (mRequest != nullptr)
  335. {
  336. ComplexArgumentParser::Finalize(*mRequest);
  337. }
  338. }
  339. void SetArgument(T * request) { mRequest = request; };
  340. CHIP_ERROR Parse(const char * label, const char * json)
  341. {
  342. Json::Value value;
  343. if (strcmp(kNullString, json) == 0)
  344. {
  345. value = Json::nullValue;
  346. }
  347. else if (!JsonParser::ParseComplexArgument(label, json, value))
  348. {
  349. return CHIP_ERROR_INVALID_ARGUMENT;
  350. }
  351. return ComplexArgumentParser::Setup(label, *mRequest, value);
  352. }
  353. void Reset() { *mRequest = T(); }
  354. private:
  355. T * mRequest;
  356. };