CustomArgument.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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. #pragma once
  19. #include <app-common/zap-generated/cluster-objects.h>
  20. #include <commands/common/HexConversion.h>
  21. #include <lib/support/BytesToHex.h>
  22. #include <lib/support/CHIPMemString.h>
  23. #include <lib/support/SafeInt.h>
  24. #include "JsonParser.h"
  25. namespace {
  26. static constexpr char kPayloadHexPrefix[] = "hex:";
  27. static constexpr char kPayloadSignedPrefix[] = "s:";
  28. static constexpr char kPayloadUnsignedPrefix[] = "u:";
  29. static constexpr char kPayloadFloatPrefix[] = "f:";
  30. static constexpr char kPayloadDoublePrefix[] = "d:";
  31. static constexpr size_t kPayloadHexPrefixLen = ArraySize(kPayloadHexPrefix) - 1; // ignore null character
  32. static constexpr size_t kPayloadSignedPrefixLen = ArraySize(kPayloadSignedPrefix) - 1; // ignore null character
  33. static constexpr size_t kPayloadUnsignedPrefixLen = ArraySize(kPayloadUnsignedPrefix) - 1; // ignore null character
  34. static constexpr size_t kPayloadFloatPrefixLen = ArraySize(kPayloadFloatPrefix) - 1; // ignore null character
  35. static constexpr size_t kPayloadDoublePrefixLen = ArraySize(kPayloadDoublePrefix) - 1; // ignore null character
  36. } // namespace
  37. class CustomArgumentParser
  38. {
  39. public:
  40. static CHIP_ERROR Put(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  41. {
  42. if (value.isObject())
  43. {
  44. return CustomArgumentParser::PutObject(writer, tag, value);
  45. }
  46. if (value.isArray())
  47. {
  48. return CustomArgumentParser::PutArray(writer, tag, value);
  49. }
  50. if (value.isString())
  51. {
  52. if (IsOctetString(value))
  53. {
  54. return CustomArgumentParser::PutOctetString(writer, tag, value);
  55. }
  56. if (IsUnsignedNumberPrefix(value))
  57. {
  58. return CustomArgumentParser::PutUnsignedFromString(writer, tag, value);
  59. }
  60. if (IsSignedNumberPrefix(value))
  61. {
  62. return CustomArgumentParser::PutSignedFromString(writer, tag, value);
  63. }
  64. if (IsFloatNumberPrefix(value))
  65. {
  66. return CustomArgumentParser::PutFloatFromString(writer, tag, value);
  67. }
  68. if (IsDoubleNumberPrefix(value))
  69. {
  70. return CustomArgumentParser::PutDoubleFromString(writer, tag, value);
  71. }
  72. return CustomArgumentParser::PutCharString(writer, tag, value);
  73. }
  74. if (value.isNull())
  75. {
  76. return chip::app::DataModel::Encode(*writer, tag, chip::app::DataModel::Nullable<uint8_t>());
  77. }
  78. if (value.isBool())
  79. {
  80. return chip::app::DataModel::Encode(*writer, tag, value.asBool());
  81. }
  82. if (value.isUInt())
  83. {
  84. return chip::app::DataModel::Encode(*writer, tag, value.asLargestUInt());
  85. }
  86. if (value.isInt())
  87. {
  88. return chip::app::DataModel::Encode(*writer, tag, value.asLargestInt());
  89. }
  90. if (value.isNumeric())
  91. {
  92. return chip::app::DataModel::Encode(*writer, tag, value.asDouble());
  93. }
  94. return CHIP_ERROR_NOT_IMPLEMENTED;
  95. }
  96. private:
  97. static CHIP_ERROR PutArray(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  98. {
  99. chip::TLV::TLVType outer;
  100. ReturnErrorOnFailure(writer->StartContainer(tag, chip::TLV::kTLVType_Array, outer));
  101. Json::ArrayIndex size = value.size();
  102. for (Json::ArrayIndex i = 0; i < size; i++)
  103. {
  104. ReturnErrorOnFailure(CustomArgumentParser::Put(writer, chip::TLV::AnonymousTag(), value[i]));
  105. }
  106. return writer->EndContainer(outer);
  107. }
  108. static CHIP_ERROR PutObject(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  109. {
  110. chip::TLV::TLVType outer;
  111. ReturnErrorOnFailure(writer->StartContainer(tag, chip::TLV::kTLVType_Structure, outer));
  112. for (auto const & id : value.getMemberNames())
  113. {
  114. auto index = std::stoul(id, nullptr, 0);
  115. VerifyOrReturnError(chip::CanCastTo<uint8_t>(index), CHIP_ERROR_INVALID_ARGUMENT);
  116. ReturnErrorOnFailure(CustomArgumentParser::Put(writer, chip::TLV::ContextTag(static_cast<uint8_t>(index)), value[id]));
  117. }
  118. return writer->EndContainer(outer);
  119. }
  120. static CHIP_ERROR PutOctetString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  121. {
  122. const char * hexData = value.asCString() + kPayloadHexPrefixLen;
  123. size_t hexDataLen = strlen(hexData);
  124. chip::Platform::ScopedMemoryBuffer<uint8_t> buffer;
  125. size_t octetCount;
  126. ReturnErrorOnFailure(HexToBytes(
  127. chip::CharSpan(hexData, hexDataLen),
  128. [&buffer](size_t allocSize) {
  129. buffer.Calloc(allocSize);
  130. return buffer.Get();
  131. },
  132. &octetCount));
  133. return chip::app::DataModel::Encode(*writer, tag, chip::ByteSpan(buffer.Get(), octetCount));
  134. }
  135. static CHIP_ERROR PutCharString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  136. {
  137. size_t size = strlen(value.asCString());
  138. return chip::app::DataModel::Encode(*writer, tag, chip::CharSpan(value.asCString(), size));
  139. }
  140. static CHIP_ERROR PutUnsignedFromString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  141. {
  142. char numberAsString[21];
  143. chip::Platform::CopyString(numberAsString, value.asCString() + kPayloadUnsignedPrefixLen);
  144. auto number = std::stoull(numberAsString, nullptr, 0);
  145. return chip::app::DataModel::Encode(*writer, tag, static_cast<uint64_t>(number));
  146. }
  147. static CHIP_ERROR PutSignedFromString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  148. {
  149. char numberAsString[21];
  150. chip::Platform::CopyString(numberAsString, value.asCString() + kPayloadSignedPrefixLen);
  151. auto number = std::stoll(numberAsString, nullptr, 0);
  152. return chip::app::DataModel::Encode(*writer, tag, static_cast<int64_t>(number));
  153. }
  154. static CHIP_ERROR PutFloatFromString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  155. {
  156. char numberAsString[21];
  157. chip::Platform::CopyString(numberAsString, value.asCString() + kPayloadFloatPrefixLen);
  158. auto number = std::stof(numberAsString);
  159. return chip::app::DataModel::Encode(*writer, tag, number);
  160. }
  161. static CHIP_ERROR PutDoubleFromString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
  162. {
  163. char numberAsString[21];
  164. chip::Platform::CopyString(numberAsString, value.asCString() + kPayloadDoublePrefixLen);
  165. auto number = std::stod(numberAsString);
  166. return chip::app::DataModel::Encode(*writer, tag, number);
  167. }
  168. static bool IsOctetString(Json::Value & value)
  169. {
  170. return (strncmp(value.asCString(), kPayloadHexPrefix, kPayloadHexPrefixLen) == 0);
  171. }
  172. static bool IsUnsignedNumberPrefix(Json::Value & value)
  173. {
  174. return (strncmp(value.asCString(), kPayloadUnsignedPrefix, kPayloadUnsignedPrefixLen) == 0);
  175. }
  176. static bool IsSignedNumberPrefix(Json::Value & value)
  177. {
  178. return (strncmp(value.asCString(), kPayloadSignedPrefix, kPayloadSignedPrefixLen) == 0);
  179. }
  180. static bool IsFloatNumberPrefix(Json::Value & value)
  181. {
  182. return (strncmp(value.asCString(), kPayloadFloatPrefix, kPayloadFloatPrefixLen) == 0);
  183. }
  184. static bool IsDoubleNumberPrefix(Json::Value & value)
  185. {
  186. return (strncmp(value.asCString(), kPayloadDoublePrefix, kPayloadDoublePrefixLen) == 0);
  187. }
  188. };
  189. class CustomArgument
  190. {
  191. public:
  192. ~CustomArgument()
  193. {
  194. if (mData != nullptr)
  195. {
  196. chip::Platform::MemoryFree(mData);
  197. }
  198. }
  199. CHIP_ERROR Parse(const char * label, const char * json)
  200. {
  201. Json::Value value;
  202. constexpr const char kHexNumPrefix[] = "0x";
  203. constexpr size_t kHexNumPrefixLen = ArraySize(kHexNumPrefix) - 1;
  204. if (strncmp(json, kPayloadHexPrefix, kPayloadHexPrefixLen) == 0 ||
  205. strncmp(json, kPayloadSignedPrefix, kPayloadSignedPrefixLen) == 0 ||
  206. strncmp(json, kPayloadUnsignedPrefix, kPayloadUnsignedPrefixLen) == 0 ||
  207. strncmp(json, kPayloadFloatPrefix, kPayloadFloatPrefixLen) == 0 ||
  208. strncmp(json, kPayloadDoublePrefix, kPayloadDoublePrefixLen) == 0)
  209. {
  210. value = Json::Value(json);
  211. }
  212. else if (strncmp(json, kHexNumPrefix, kHexNumPrefixLen) == 0)
  213. {
  214. // Assume that hex numbers are unsigned. Prepend
  215. // kPayloadUnsignedPrefix and then let the rest of the logic handle
  216. // things.
  217. std::string str(kPayloadUnsignedPrefix);
  218. str += json;
  219. value = Json::Value(str);
  220. }
  221. else if (!JsonParser::ParseCustomArgument(label, json, value))
  222. {
  223. return CHIP_ERROR_INVALID_ARGUMENT;
  224. }
  225. mData = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(sizeof(uint8_t), mDataMaxLen));
  226. VerifyOrReturnError(mData != nullptr, CHIP_ERROR_NO_MEMORY);
  227. chip::TLV::TLVWriter writer;
  228. writer.Init(mData, mDataMaxLen);
  229. ReturnErrorOnFailure(CustomArgumentParser::Put(&writer, chip::TLV::AnonymousTag(), value));
  230. mDataLen = writer.GetLengthWritten();
  231. return writer.Finalize();
  232. }
  233. CHIP_ERROR Encode(chip::TLV::TLVWriter & writer, chip::TLV::Tag tag) const
  234. {
  235. chip::TLV::TLVReader reader;
  236. reader.Init(mData, mDataLen);
  237. ReturnErrorOnFailure(reader.Next());
  238. return writer.CopyElement(tag, reader);
  239. }
  240. // We trust our consumers to do the encoding of our data correctly, so don't
  241. // need to know whether we are being encoded for a write.
  242. static constexpr bool kIsFabricScoped = false;
  243. private:
  244. uint8_t * mData = nullptr;
  245. uint32_t mDataLen = 0;
  246. static constexpr uint32_t mDataMaxLen = 4096;
  247. };