| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- * Copyright (c) 2022 Project CHIP Authors
- * All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- /**
- * This file allocate/free memory using the chip platform abstractions
- * (Platform::MemoryCalloc and Platform::MemoryFree) for hosting a subset of the
- * data model internal types until they are consumed by the DataModel::Encode machinery:
- * - chip::app:DataModel::List<T>
- * - chip::ByteSpan
- * - chip::CharSpan
- *
- * Memory allocation happens during the 'Setup' phase, while memory deallocation happens
- * during the 'Finalize' phase.
- *
- * The 'Finalize' phase during the destructor phase, and if needed, 'Finalize' will call
- * the 'Finalize' phase of its descendant.
- */
- #pragma once
- #include <app-common/zap-generated/cluster-objects.h>
- #include <app/data-model/List.h>
- #include <app/data-model/Nullable.h>
- #include <commands/common/HexConversion.h>
- #include <json/json.h>
- #include <lib/core/Optional.h>
- #include <lib/support/BytesToHex.h>
- #include <lib/support/CHIPMemString.h>
- #include <lib/support/SafeInt.h>
- #include "JsonParser.h"
- inline constexpr uint8_t kMaxLabelLength = UINT8_MAX;
- inline constexpr const char kNullString[] = "null";
- class ComplexArgumentParser
- {
- public:
- ComplexArgumentParser() {}
- template <typename T,
- typename std::enable_if_t<std::is_integral<T>::value && !std::is_signed<T>::value &&
- !std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, bool>::value,
- int> = 0>
- static CHIP_ERROR Setup(const char * label, T & request, Json::Value value)
- {
- if (value.isNumeric())
- {
- if (chip::CanCastTo<T>(value.asLargestUInt()))
- {
- request = static_cast<T>(value.asLargestUInt());
- return CHIP_NO_ERROR;
- }
- }
- else if (value.isString())
- {
- // Check for a hex number; JSON does not support those as numbers,
- // so they have to be done as strings. And we might as well support
- // string-encoded unsigned numbers in general if we're doing that.
- bool isHexNotation = strncmp(value.asCString(), "0x", 2) == 0 || strncmp(value.asCString(), "0X", 2) == 0;
- std::stringstream str;
- isHexNotation ? str << std::hex << value.asCString() : str << value.asCString();
- uint64_t val;
- str >> val;
- if (!str.fail() && str.eof() && chip::CanCastTo<T>(val))
- {
- request = static_cast<T>(val);
- return CHIP_NO_ERROR;
- }
- }
- ChipLogError(chipTool, "Error while encoding %s as an unsigned integer.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- template <typename T, std::enable_if_t<std::is_signed<T>::value, bool> = true>
- static CHIP_ERROR Setup(const char * label, T & request, Json::Value value)
- {
- if (!value.isNumeric() || !chip::CanCastTo<T>(value.asLargestInt()))
- {
- ChipLogError(chipTool, "Error while encoding %s as an unsigned integer.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- request = static_cast<T>(value.asLargestInt());
- return CHIP_NO_ERROR;
- }
- template <typename T, typename std::enable_if_t<std::is_enum<T>::value, int> = 0>
- static CHIP_ERROR Setup(const char * label, T & request, Json::Value value)
- {
- std::underlying_type_t<T> requestValue;
- ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
- request = static_cast<T>(requestValue);
- return CHIP_NO_ERROR;
- }
- template <typename T>
- static CHIP_ERROR Setup(const char * label, chip::BitFlags<T> & request, Json::Value & value)
- {
- T requestValue;
- ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
- request = chip::BitFlags<T>(requestValue);
- return CHIP_NO_ERROR;
- }
- template <typename T>
- static CHIP_ERROR Setup(const char * label, chip::BitMask<T> & request, Json::Value & value)
- {
- T requestValue;
- ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
- request = chip::BitMask<T>(requestValue);
- return CHIP_NO_ERROR;
- }
- template <typename T>
- static CHIP_ERROR Setup(const char * label, chip::Optional<T> & request, Json::Value & value)
- {
- T requestValue;
- ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
- request = chip::Optional<T>(requestValue);
- return CHIP_NO_ERROR;
- }
- template <typename T>
- static CHIP_ERROR Setup(const char * label, chip::app::DataModel::Nullable<T> & request, Json::Value & value)
- {
- if (value.isNull())
- {
- request.SetNull();
- return CHIP_NO_ERROR;
- }
- T requestValue;
- ReturnErrorOnFailure(ComplexArgumentParser::Setup(label, requestValue, value));
- request = chip::app::DataModel::Nullable<T>(requestValue);
- return CHIP_NO_ERROR;
- }
- template <typename T>
- static CHIP_ERROR Setup(const char * label, chip::app::DataModel::List<T> & request, Json::Value & value)
- {
- if (!value.isArray())
- {
- ChipLogError(chipTool, "Error while encoding %s as an array.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- auto content = static_cast<typename std::remove_const<T>::type *>(chip::Platform::MemoryCalloc(value.size(), sizeof(T)));
- VerifyOrReturnError(content != nullptr, CHIP_ERROR_NO_MEMORY);
- Json::ArrayIndex size = value.size();
- for (Json::ArrayIndex i = 0; i < size; i++)
- {
- char labelWithIndex[kMaxLabelLength];
- // GCC 7.0.1 has introduced some new warnings for snprintf (-Werror=format-truncation) by default.
- // This is not particularly useful when using snprintf and especially in this context, so in order
- // to disable the warning the %s is constrained to be of max length: (254 - 11 - 2) where:
- // - 254 is kMaxLabelLength - 1 (for null)
- // - 11 is the maximum length of a %d (-2147483648, 2147483647)
- // - 2 is the length for the "[" and "]" characters.
- snprintf(labelWithIndex, sizeof(labelWithIndex), "%.241s[%d]", label, i);
- ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithIndex, content[i], value[i]));
- }
- request = chip::app::DataModel::List<T>(content, value.size());
- return CHIP_NO_ERROR;
- }
- static CHIP_ERROR Setup(const char * label, chip::ByteSpan & request, Json::Value & value)
- {
- if (!value.isString())
- {
- ChipLogError(chipTool, "Error while encoding %s as an octet string: Not a string.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- auto str = value.asString();
- auto size = str.size();
- uint8_t * buffer = nullptr;
- if (IsStrString(str.c_str()))
- {
- // Skip the prefix
- str.erase(0, kStrStringPrefixLen);
- size = str.size();
- buffer = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(size, sizeof(uint8_t)));
- VerifyOrReturnError(buffer != nullptr, CHIP_ERROR_NO_MEMORY);
- memcpy(buffer, str.c_str(), size);
- }
- else
- {
- if (IsHexString(str.c_str()))
- {
- // Skip the prefix
- str.erase(0, kHexStringPrefixLen);
- size = str.size();
- }
- CHIP_ERROR err = HexToBytes(
- chip::CharSpan(str.c_str(), size),
- [&buffer](size_t allocSize) {
- buffer = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(allocSize, sizeof(uint8_t)));
- return buffer;
- },
- &size);
- if (err != CHIP_NO_ERROR)
- {
- if (buffer != nullptr)
- {
- chip::Platform::MemoryFree(buffer);
- }
- return err;
- }
- }
- request = chip::ByteSpan(buffer, size);
- return CHIP_NO_ERROR;
- }
- static CHIP_ERROR Setup(const char * label, chip::CharSpan & request, Json::Value & value)
- {
- if (!value.isString())
- {
- ChipLogError(chipTool, "Error while encoding %s as a string: Not a string.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- size_t size = strlen(value.asCString());
- auto buffer = static_cast<char *>(chip::Platform::MemoryCalloc(size, sizeof(char)));
- VerifyOrReturnError(buffer != nullptr, CHIP_ERROR_NO_MEMORY);
- memcpy(buffer, value.asCString(), size);
- request = chip::CharSpan(buffer, size);
- return CHIP_NO_ERROR;
- }
- static CHIP_ERROR Setup(const char * label, float & request, Json::Value & value)
- {
- if (!value.isNumeric())
- {
- ChipLogError(chipTool, "Error while encoding %s as a float: Not a number.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- request = static_cast<float>(value.asFloat());
- return CHIP_NO_ERROR;
- }
- static CHIP_ERROR Setup(const char * label, double & request, Json::Value & value)
- {
- if (!value.isNumeric())
- {
- ChipLogError(chipTool, "Error while encoding %s as a double: Not a number.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- request = static_cast<double>(value.asDouble());
- return CHIP_NO_ERROR;
- }
- static CHIP_ERROR Setup(const char * label, bool & request, Json::Value & value)
- {
- if (!value.isBool())
- {
- ChipLogError(chipTool, "Error while encoding %s as a boolean: Not a boolean.", label);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- request = value.asBool();
- return CHIP_NO_ERROR;
- }
- static CHIP_ERROR EnsureMemberExist(const char * label, const char * memberName, bool hasMember)
- {
- if (hasMember)
- {
- return CHIP_NO_ERROR;
- }
- ChipLogError(chipTool, "%s is required. Should be provided as {\"%s\": value}", label, memberName);
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- static CHIP_ERROR EnsureNoMembersRemaining(const char * label, const Json::Value & value)
- {
- auto remainingFields = value.getMemberNames();
- if (remainingFields.size() == 0)
- {
- return CHIP_NO_ERROR;
- }
- #if CHIP_ERROR_LOGGING
- for (auto & field : remainingFields)
- {
- ChipLogError(chipTool, "Unexpected field name: '%s.%s'", label, field.c_str());
- }
- #endif // CHIP_ERROR_LOGGING
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- template <typename T>
- static void Finalize(T & request)
- {
- // Nothing to do
- }
- template <typename T>
- static void Finalize(chip::Optional<T> & request)
- {
- VerifyOrReturn(request.HasValue());
- ComplexArgumentParser::Finalize(request.Value());
- }
- template <typename T>
- static void Finalize(chip::app::DataModel::Nullable<T> & request)
- {
- VerifyOrReturn(!request.IsNull());
- ComplexArgumentParser::Finalize(request.Value());
- }
- static void Finalize(chip::ByteSpan & request)
- {
- VerifyOrReturn(request.data() != nullptr);
- chip::Platform::MemoryFree(reinterpret_cast<void *>(const_cast<uint8_t *>(request.data())));
- }
- static void Finalize(chip::CharSpan & request)
- {
- VerifyOrReturn(request.data() != nullptr);
- chip::Platform::MemoryFree(reinterpret_cast<void *>(const_cast<char *>(request.data())));
- }
- template <typename T>
- static void Finalize(chip::app::DataModel::List<T> & request)
- {
- VerifyOrReturn(request.data() != nullptr);
- size_t size = request.size();
- auto data = const_cast<typename std::remove_const<T>::type *>(request.data());
- for (size_t i = 0; i < size; i++)
- {
- Finalize(data[i]);
- }
- chip::Platform::MemoryFree(reinterpret_cast<void *>(data));
- }
- #include <zap-generated/cluster/ComplexArgumentParser.h>
- };
- class ComplexArgument
- {
- public:
- virtual ~ComplexArgument() {}
- virtual CHIP_ERROR Parse(const char * label, const char * json) = 0;
- virtual void Reset() = 0;
- };
- template <typename T>
- class TypedComplexArgument : public ComplexArgument
- {
- public:
- TypedComplexArgument() {}
- TypedComplexArgument(T * request) : mRequest(request) {}
- ~TypedComplexArgument()
- {
- if (mRequest != nullptr)
- {
- ComplexArgumentParser::Finalize(*mRequest);
- }
- }
- void SetArgument(T * request) { mRequest = request; };
- CHIP_ERROR Parse(const char * label, const char * json)
- {
- Json::Value value;
- if (strcmp(kNullString, json) == 0)
- {
- value = Json::nullValue;
- }
- else if (!JsonParser::ParseComplexArgument(label, json, value))
- {
- return CHIP_ERROR_INVALID_ARGUMENT;
- }
- return ComplexArgumentParser::Setup(label, *mRequest, value);
- }
- void Reset() { *mRequest = T(); }
- private:
- T * mRequest;
- };
|