Matter SDK Coverage Report
Current view: top level - lib/support/jsontlv - JsonToTlv.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 96.6 % 203 196
Test Date: 2025-01-17 19:00:11 Functions: 91.7 % 12 11

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2023 Project CHIP Authors
       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              : #include <stdint.h>
      19              : 
      20              : #include <algorithm>
      21              : #include <charconv>
      22              : #include <sstream>
      23              : #include <string>
      24              : #include <vector>
      25              : 
      26              : #include <json/json.h>
      27              : #include <lib/support/Base64.h>
      28              : #include <lib/support/SafeInt.h>
      29              : #include <lib/support/jsontlv/ElementTypes.h>
      30              : #include <lib/support/jsontlv/JsonToTlv.h>
      31              : 
      32              : namespace chip {
      33              : 
      34              : namespace {
      35              : 
      36              : // Not directly used: TLV encoding will not encode this number and
      37              : // will just encode "Implicit profile tag"
      38              : // This profile, but will be used for deciding what binary values to encode.
      39              : constexpr uint32_t kTemporaryImplicitProfileId = 0xFF01;
      40              : 
      41          337 : std::vector<std::string> SplitIntoFieldsBySeparator(const std::string & input, char separator)
      42              : {
      43          337 :     std::vector<std::string> substrings;
      44          337 :     std::stringstream ss(input);
      45          337 :     std::string substring;
      46              : 
      47         1074 :     while (std::getline(ss, substring, separator))
      48              :     {
      49          737 :         substrings.push_back(std::move(substring));
      50              :     }
      51              : 
      52          337 :     return substrings;
      53          337 : }
      54              : 
      55          325 : CHIP_ERROR JsonTypeStrToTlvType(const char * elementType, ElementTypeContext & type)
      56              : {
      57          325 :     if (strcmp(elementType, kElementTypeInt) == 0)
      58              :     {
      59           55 :         type.tlvType = TLV::kTLVType_SignedInteger;
      60              :     }
      61          270 :     else if (strcmp(elementType, kElementTypeUInt) == 0)
      62              :     {
      63           50 :         type.tlvType = TLV::kTLVType_UnsignedInteger;
      64              :     }
      65          220 :     else if (strcmp(elementType, kElementTypeBool) == 0)
      66              :     {
      67           30 :         type.tlvType = TLV::kTLVType_Boolean;
      68              :     }
      69          190 :     else if (strcmp(elementType, kElementTypeFloat) == 0)
      70              :     {
      71           29 :         type.tlvType  = TLV::kTLVType_FloatingPointNumber;
      72           29 :         type.isDouble = false;
      73              :     }
      74          161 :     else if (strcmp(elementType, kElementTypeDouble) == 0)
      75              :     {
      76           29 :         type.tlvType  = TLV::kTLVType_FloatingPointNumber;
      77           29 :         type.isDouble = true;
      78              :     }
      79          132 :     else if (strcmp(elementType, kElementTypeBytes) == 0)
      80              :     {
      81           28 :         type.tlvType = TLV::kTLVType_ByteString;
      82              :     }
      83          104 :     else if (strcmp(elementType, kElementTypeString) == 0)
      84              :     {
      85           22 :         type.tlvType = TLV::kTLVType_UTF8String;
      86              :     }
      87           82 :     else if (strcmp(elementType, kElementTypeNull) == 0)
      88              :     {
      89            8 :         type.tlvType = TLV::kTLVType_Null;
      90              :     }
      91           74 :     else if (strcmp(elementType, kElementTypeStruct) == 0)
      92              :     {
      93           24 :         type.tlvType = TLV::kTLVType_Structure;
      94              :     }
      95           50 :     else if (strncmp(elementType, kElementTypeArray, strlen(kElementTypeArray)) == 0)
      96              :     {
      97           49 :         type.tlvType = TLV::kTLVType_Array;
      98              :     }
      99              :     else
     100              :     {
     101            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
     102              :     }
     103              : 
     104          324 :     return CHIP_NO_ERROR;
     105              : }
     106              : 
     107              : struct ElementContext
     108              : {
     109              :     std::string jsonName;
     110              :     TLV::Tag tag = TLV::AnonymousTag();
     111              :     ElementTypeContext type;
     112              :     ElementTypeContext subType;
     113              : };
     114              : 
     115          322 : bool CompareByTag(const ElementContext & a, const ElementContext & b)
     116              : {
     117              :     // If tags are of the same type compare by tag number
     118          322 :     if (IsContextTag(a.tag) == IsContextTag(b.tag))
     119              :     {
     120          310 :         return TLV::TagNumFromTag(a.tag) < TLV::TagNumFromTag(b.tag);
     121              :     }
     122              :     // Otherwise, compare by tag type: context tags first followed by common profile tags
     123           12 :     return IsContextTag(a.tag);
     124              : }
     125              : 
     126              : // The profileId parameter is used when encoding a tag for a TLV element to specify the profile that the tag belongs to.
     127              : // If the vendor ID is zero but the tag ID does not fit within an 8-bit value, the function uses Implicit Profile Tag.
     128              : // Here, the kTemporaryImplicitProfileId serves as a default value for cases where no explicit profile ID is provided by
     129              : // the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead
     130              : // associated with a temporary implicit profile ID (0xFF01).
     131          285 : CHIP_ERROR InternalConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag, const uint32_t profileId = kTemporaryImplicitProfileId)
     132              : {
     133          285 :     uint16_t vendor_id = static_cast<uint16_t>(tagNumber >> 16);
     134          285 :     uint16_t tag_id    = static_cast<uint16_t>(tagNumber & 0xFFFF);
     135              : 
     136          285 :     if (vendor_id != 0)
     137              :     {
     138           18 :         tag = TLV::ProfileTag(vendor_id, /*profileNum=*/0, tag_id);
     139              :     }
     140          267 :     else if (tag_id <= UINT8_MAX)
     141              :     {
     142          256 :         tag = TLV::ContextTag(static_cast<uint8_t>(tagNumber));
     143              :     }
     144              :     else
     145              :     {
     146           11 :         tag = TLV::ProfileTag(profileId, tagNumber);
     147              :     }
     148          285 :     return CHIP_NO_ERROR;
     149              : }
     150              : 
     151              : template <typename T>
     152          305 : CHIP_ERROR ParseNumericalField(const std::string & decimalString, T & outValue)
     153              : {
     154          305 :     const char * start_ptr       = decimalString.data();
     155          305 :     const char * end_ptr         = decimalString.data() + decimalString.size();
     156          305 :     auto [last_converted_ptr, _] = std::from_chars(start_ptr, end_ptr, outValue, 10);
     157          305 :     VerifyOrReturnError(last_converted_ptr == end_ptr, CHIP_ERROR_INVALID_ARGUMENT);
     158          303 :     return CHIP_NO_ERROR;
     159              : }
     160              : 
     161          288 : CHIP_ERROR ParseJsonName(const std::string & name, ElementContext & elementCtx, uint32_t implicitProfileId)
     162              : {
     163          288 :     uint32_t tagNumber                  = 0;
     164          288 :     const char * elementType            = nullptr;
     165          288 :     std::vector<std::string> nameFields = SplitIntoFieldsBySeparator(name, ':');
     166          288 :     TLV::Tag tag                        = TLV::AnonymousTag();
     167          288 :     ElementTypeContext type;
     168          288 :     ElementTypeContext subType;
     169              : 
     170          288 :     if (nameFields.size() == 2)
     171              :     {
     172          223 :         ReturnErrorOnFailure(ParseNumericalField(nameFields[0], tagNumber));
     173          221 :         elementType = nameFields[1].c_str();
     174              :     }
     175           65 :     else if (nameFields.size() == 3)
     176              :     {
     177           64 :         ReturnErrorOnFailure(ParseNumericalField(nameFields[1], tagNumber));
     178           64 :         elementType = nameFields[2].c_str();
     179              :     }
     180              :     else
     181              :     {
     182            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
     183              :     }
     184              : 
     185          285 :     ReturnErrorOnFailure(InternalConvertTlvTag(tagNumber, tag, implicitProfileId));
     186          285 :     ReturnErrorOnFailure(JsonTypeStrToTlvType(elementType, type));
     187              : 
     188          284 :     if (type.tlvType == TLV::kTLVType_Array)
     189              :     {
     190           98 :         std::vector<std::string> arrayFields = SplitIntoFieldsBySeparator(elementType, '-');
     191           49 :         VerifyOrReturnError(arrayFields.size() == 2, CHIP_ERROR_INVALID_ARGUMENT);
     192              : 
     193           49 :         if (strcmp(arrayFields[1].c_str(), kElementTypeEmpty) == 0)
     194              :         {
     195            9 :             subType.tlvType = TLV::kTLVType_NotSpecified;
     196              :         }
     197              :         else
     198              :         {
     199           40 :             ReturnErrorOnFailure(JsonTypeStrToTlvType(arrayFields[1].c_str(), subType));
     200              :         }
     201           49 :     }
     202              : 
     203          284 :     elementCtx.jsonName = name;
     204          284 :     elementCtx.tag      = tag;
     205          284 :     elementCtx.type     = type;
     206          284 :     elementCtx.subType  = subType;
     207              : 
     208          284 :     return CHIP_NO_ERROR;
     209          288 : }
     210              : 
     211          545 : CHIP_ERROR EncodeTlvElement(const Json::Value & val, TLV::TLVWriter & writer, const ElementContext & elementCtx)
     212              : {
     213          545 :     TLV::Tag tag = elementCtx.tag;
     214              : 
     215          545 :     switch (elementCtx.type.tlvType)
     216              :     {
     217           64 :     case TLV::kTLVType_UnsignedInteger: {
     218           64 :         uint64_t v = 0;
     219           64 :         if (val.isUInt64())
     220              :         {
     221           53 :             v = val.asUInt64();
     222              :         }
     223           11 :         else if (val.isString())
     224              :         {
     225           10 :             ReturnErrorOnFailure(ParseNumericalField(val.asString(), v));
     226              :         }
     227              :         else
     228              :         {
     229            1 :             return CHIP_ERROR_INVALID_ARGUMENT;
     230              :         }
     231           63 :         ReturnErrorOnFailure(writer.Put(tag, v));
     232           63 :         break;
     233              :     }
     234              : 
     235           91 :     case TLV::kTLVType_SignedInteger: {
     236           91 :         int64_t v = 0;
     237           91 :         if (val.isInt64())
     238              :         {
     239           83 :             v = val.asInt64();
     240              :         }
     241            8 :         else if (val.isString())
     242              :         {
     243            8 :             ReturnErrorOnFailure(ParseNumericalField(val.asString(), v));
     244              :         }
     245              :         else
     246              :         {
     247            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     248              :         }
     249           91 :         ReturnErrorOnFailure(writer.Put(tag, v));
     250           91 :         break;
     251              :     }
     252              : 
     253           38 :     case TLV::kTLVType_Boolean: {
     254           38 :         VerifyOrReturnError(val.isBool(), CHIP_ERROR_INVALID_ARGUMENT);
     255           37 :         ReturnErrorOnFailure(writer.Put(tag, val.asBool()));
     256           37 :         break;
     257              :     }
     258              : 
     259           76 :     case TLV::kTLVType_FloatingPointNumber: {
     260           76 :         if (val.isNumeric())
     261              :         {
     262           62 :             if (elementCtx.type.isDouble)
     263              :             {
     264           36 :                 ReturnErrorOnFailure(writer.Put(tag, val.asDouble()));
     265              :             }
     266              :             else
     267              :             {
     268           26 :                 ReturnErrorOnFailure(writer.Put(tag, val.asFloat()));
     269              :             }
     270              :         }
     271           14 :         else if (val.isString())
     272              :         {
     273           14 :             const std::string valAsString = val.asString();
     274           14 :             bool isPositiveInfinity       = (valAsString == kFloatingPointPositiveInfinity);
     275           14 :             bool isNegativeInfinity       = (valAsString == kFloatingPointNegativeInfinity);
     276           14 :             VerifyOrReturnError(isPositiveInfinity || isNegativeInfinity, CHIP_ERROR_INVALID_ARGUMENT);
     277           12 :             if (elementCtx.type.isDouble)
     278              :             {
     279            6 :                 if (isPositiveInfinity)
     280              :                 {
     281            4 :                     ReturnErrorOnFailure(writer.Put(tag, std::numeric_limits<double>::infinity()));
     282              :                 }
     283              :                 else
     284              :                 {
     285            2 :                     ReturnErrorOnFailure(writer.Put(tag, -std::numeric_limits<double>::infinity()));
     286              :                 }
     287              :             }
     288              :             else
     289              :             {
     290            6 :                 if (isPositiveInfinity)
     291              :                 {
     292            2 :                     ReturnErrorOnFailure(writer.Put(tag, std::numeric_limits<float>::infinity()));
     293              :                 }
     294              :                 else
     295              :                 {
     296            4 :                     ReturnErrorOnFailure(writer.Put(tag, -std::numeric_limits<float>::infinity()));
     297              :                 }
     298              :             }
     299           14 :         }
     300              :         else
     301              :         {
     302            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     303              :         }
     304           74 :         break;
     305              :     }
     306              : 
     307           32 :     case TLV::kTLVType_ByteString: {
     308           39 :         VerifyOrReturnError(val.isString(), CHIP_ERROR_INVALID_ARGUMENT);
     309           32 :         const std::string valAsString = val.asString();
     310           32 :         size_t encodedLen             = valAsString.length();
     311           32 :         VerifyOrReturnError(CanCastTo<uint16_t>(encodedLen), CHIP_ERROR_INVALID_ARGUMENT);
     312              : 
     313              :         // Check if the length is a multiple of 4 as strict padding is required.
     314           32 :         VerifyOrReturnError(encodedLen % 4 == 0, CHIP_ERROR_INVALID_ARGUMENT);
     315              : 
     316           26 :         Platform::ScopedMemoryBuffer<uint8_t> byteString;
     317           26 :         byteString.Alloc(BASE64_MAX_DECODED_LEN(static_cast<uint16_t>(encodedLen)));
     318           26 :         VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
     319              : 
     320           26 :         auto decodedLen = Base64Decode(valAsString.c_str(), static_cast<uint16_t>(encodedLen), byteString.Get());
     321           26 :         VerifyOrReturnError(decodedLen < UINT16_MAX, CHIP_ERROR_INVALID_ARGUMENT);
     322           25 :         ReturnErrorOnFailure(writer.PutBytes(tag, byteString.Get(), decodedLen));
     323           25 :         break;
     324           58 :     }
     325              : 
     326           30 :     case TLV::kTLVType_UTF8String: {
     327           30 :         VerifyOrReturnError(val.isString(), CHIP_ERROR_INVALID_ARGUMENT);
     328           30 :         const std::string valAsString = val.asString();
     329           30 :         ReturnErrorOnFailure(writer.PutString(tag, valAsString.data(), static_cast<uint32_t>(valAsString.size())));
     330           30 :         break;
     331           30 :     }
     332              : 
     333           10 :     case TLV::kTLVType_Null: {
     334           10 :         VerifyOrReturnError(val.isNull(), CHIP_ERROR_INVALID_ARGUMENT);
     335           10 :         ReturnErrorOnFailure(writer.PutNull(tag));
     336           10 :         break;
     337              :     }
     338              : 
     339          155 :     case TLV::kTLVType_Structure: {
     340              :         TLV::TLVType containerType;
     341          170 :         VerifyOrReturnError(val.isObject(), CHIP_ERROR_INVALID_ARGUMENT);
     342          155 :         ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, containerType));
     343              : 
     344          155 :         std::vector<std::string> jsonNames = val.getMemberNames();
     345          155 :         std::vector<ElementContext> nestedElementsCtx;
     346              : 
     347          439 :         for (size_t i = 0; i < jsonNames.size(); i++)
     348              :         {
     349          288 :             ElementContext ctx;
     350          288 :             ReturnErrorOnFailure(ParseJsonName(jsonNames[i], ctx, writer.ImplicitProfileId));
     351          284 :             nestedElementsCtx.push_back(ctx);
     352          288 :         }
     353              : 
     354              :         // Sort Json object elements by Tag number (low to high).
     355              :         // Note that all sorted Context Tags will appear first followed by all sorted Common Tags.
     356          151 :         std::sort(nestedElementsCtx.begin(), nestedElementsCtx.end(), CompareByTag);
     357              : 
     358          424 :         for (auto & ctx : nestedElementsCtx)
     359              :         {
     360          284 :             ReturnErrorOnFailure(EncodeTlvElement(val[ctx.jsonName], writer, ctx));
     361              :         }
     362              : 
     363          140 :         ReturnErrorOnFailure(writer.EndContainer(containerType));
     364          140 :         break;
     365          310 :     }
     366              : 
     367           49 :     case TLV::kTLVType_Array: {
     368              :         TLV::TLVType containerType;
     369           51 :         VerifyOrReturnError(val.isArray(), CHIP_ERROR_INVALID_ARGUMENT);
     370           49 :         ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Array, containerType));
     371              : 
     372           49 :         if (elementCtx.subType.tlvType == TLV::kTLVType_NotSpecified)
     373              :         {
     374            9 :             VerifyOrReturnError(val.size() == 0, CHIP_ERROR_INVALID_ARGUMENT);
     375              :         }
     376              :         else
     377              :         {
     378           40 :             ElementContext nestedElementCtx;
     379           40 :             nestedElementCtx.tag  = TLV::AnonymousTag();
     380           40 :             nestedElementCtx.type = elementCtx.subType;
     381          171 :             for (Json::ArrayIndex i = 0; i < val.size(); i++)
     382              :             {
     383          133 :                 ReturnErrorOnFailure(EncodeTlvElement(val[i], writer, nestedElementCtx));
     384              :             }
     385           40 :         }
     386              : 
     387           47 :         ReturnErrorOnFailure(writer.EndContainer(containerType));
     388           47 :         break;
     389              :     }
     390              : 
     391            0 :     default:
     392            0 :         return CHIP_ERROR_INVALID_TLV_ELEMENT;
     393              :         break;
     394              :     }
     395              : 
     396          517 :     return CHIP_NO_ERROR;
     397              : }
     398              : 
     399              : } // namespace
     400              : 
     401          108 : CHIP_ERROR JsonToTlv(const std::string & jsonString, MutableByteSpan & tlv)
     402              : {
     403          108 :     TLV::TLVWriter writer;
     404          108 :     writer.Init(tlv);
     405          108 :     writer.ImplicitProfileId = kTemporaryImplicitProfileId;
     406          108 :     ReturnErrorOnFailure(JsonToTlv(jsonString, writer));
     407           92 :     ReturnErrorOnFailure(writer.Finalize());
     408           92 :     tlv.reduce_size(writer.GetLengthWritten());
     409           92 :     return CHIP_NO_ERROR;
     410              : }
     411              : 
     412          129 : CHIP_ERROR JsonToTlv(const std::string & jsonString, TLV::TLVWriter & writer)
     413              : {
     414          129 :     Json::Reader reader;
     415          129 :     Json::Value json;
     416          129 :     bool result = reader.parse(jsonString, json);
     417          129 :     VerifyOrReturnError(result, CHIP_ERROR_INTERNAL);
     418              : 
     419          128 :     ElementContext elementCtx;
     420          128 :     elementCtx.type = { TLV::kTLVType_Structure, false };
     421              : 
     422              :     // Use kTemporaryImplicitProfileId as the default value for cases where no explicit implicit profile ID is provided by
     423              :     // the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead
     424              :     // associated with a temporary implicit profile ID (0xFF01).
     425          128 :     if (writer.ImplicitProfileId == TLV::kProfileIdNotSpecified)
     426              :     {
     427            0 :         writer.ImplicitProfileId = kTemporaryImplicitProfileId;
     428              :     }
     429              : 
     430          128 :     return EncodeTlvElement(json, writer, elementCtx);
     431          129 : }
     432              : 
     433            0 : CHIP_ERROR ConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag)
     434              : {
     435            0 :     return InternalConvertTlvTag(tagNumber, tag);
     436              : }
     437              : } // namespace chip
        

Generated by: LCOV version 2.0-1