Matter SDK Coverage Report
Current view: top level - lib/support/jsontlv - TlvToJson.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 100.0 % 156 156
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 17 17

            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 "lib/support/CHIPMemString.h"
      19              : #include "lib/support/ScopedBuffer.h"
      20              : #include <json/json.h>
      21              : #include <lib/core/DataModelTypes.h>
      22              : #include <lib/support/Base64.h>
      23              : #include <lib/support/SafeInt.h>
      24              : #include <lib/support/jsontlv/ElementTypes.h>
      25              : #include <lib/support/jsontlv/TlvToJson.h>
      26              : 
      27              : namespace chip {
      28              : 
      29              : namespace {
      30              : 
      31              : // actual value of this does not actually matter, however we need
      32              : // a value to be able to read 32-bit implicit profile tags
      33              : //
      34              : // JSON format never has this and TLV payload contains "implicit profile"
      35              : // and this value is never stored.
      36              : constexpr uint32_t kTemporaryImplicitProfileId = 0xFF01;
      37              : 
      38              : /// RAII to switch the implicit profile id for a reader
      39              : class ImplicitProfileIdChange
      40              : {
      41              : public:
      42           63 :     ImplicitProfileIdChange(TLV::TLVReader & reader, uint32_t id) : mReader(reader), mOldImplicitProfileId(reader.ImplicitProfileId)
      43              :     {
      44           63 :         reader.ImplicitProfileId = id;
      45           63 :     }
      46           63 :     ~ImplicitProfileIdChange() { mReader.ImplicitProfileId = mOldImplicitProfileId; }
      47              : 
      48              : private:
      49              :     TLV::TLVReader & mReader;
      50              :     uint32_t mOldImplicitProfileId;
      51              : };
      52              : 
      53          177 : const char * GetJsonElementStrFromType(const ElementTypeContext & ctx)
      54              : {
      55          177 :     switch (ctx.tlvType)
      56              :     {
      57           30 :     case TLV::kTLVType_UnsignedInteger:
      58           30 :         return kElementTypeUInt;
      59           25 :     case TLV::kTLVType_SignedInteger:
      60           25 :         return kElementTypeInt;
      61           17 :     case TLV::kTLVType_Boolean:
      62           17 :         return kElementTypeBool;
      63           32 :     case TLV::kTLVType_FloatingPointNumber:
      64           32 :         return ctx.isDouble ? kElementTypeDouble : kElementTypeFloat;
      65           12 :     case TLV::kTLVType_ByteString:
      66           12 :         return kElementTypeBytes;
      67           13 :     case TLV::kTLVType_UTF8String:
      68           13 :         return kElementTypeString;
      69            5 :     case TLV::kTLVType_Null:
      70            5 :         return kElementTypeNull;
      71           13 :     case TLV::kTLVType_Structure:
      72           13 :         return kElementTypeStruct;
      73           25 :     case TLV::kTLVType_Array:
      74           25 :         return kElementTypeArray;
      75            5 :     default:
      76            5 :         return kElementTypeEmpty;
      77              :     }
      78              : };
      79              : 
      80              : /*
      81              :  * Encapsulates the element information required to construct a JSON element name string in a JSON object.
      82              :  *
      83              :  * The generated JSON element name string is constructed as:
      84              :  *     'TagNumber:ElementType-SubElementType'.
      85              :  */
      86              : struct JsonObjectElementContext
      87              : {
      88          223 :     JsonObjectElementContext(TLV::TLVReader & reader)
      89          223 :     {
      90          223 :         tag               = reader.GetTag();
      91          223 :         implicitProfileId = reader.ImplicitProfileId;
      92          223 :         type.tlvType      = reader.GetType();
      93          223 :         if (type.tlvType == TLV::kTLVType_FloatingPointNumber)
      94              :         {
      95           41 :             type.isDouble = reader.IsElementDouble();
      96              :         }
      97          223 :     }
      98              : 
      99          152 :     std::string GenerateJsonElementName() const
     100              :     {
     101          152 :         std::string str = "???";
     102          152 :         if (TLV::IsContextTag(tag))
     103              :         {
     104              :             // common case for context tags: raw value
     105          140 :             str = std::to_string(TLV::TagNumFromTag(tag));
     106              :         }
     107           12 :         else if (TLV::IsProfileTag(tag))
     108              :         {
     109           12 :             if (TLV::ProfileIdFromTag(tag) == implicitProfileId)
     110              :             {
     111            5 :                 str = std::to_string(TLV::TagNumFromTag(tag));
     112              :             }
     113              :             else
     114              :             {
     115            7 :                 uint32_t tagNumber = (static_cast<uint32_t>(TLV::VendorIdFromTag(tag)) << 16) | TLV::TagNumFromTag(tag);
     116            7 :                 str                = std::to_string(tagNumber);
     117              :             }
     118              :         }
     119          152 :         str = str + ":" + GetJsonElementStrFromType(type);
     120          152 :         if (type.tlvType == TLV::kTLVType_Array)
     121              :         {
     122           25 :             str = str + "-" + GetJsonElementStrFromType(subType);
     123              :         }
     124          152 :         return str;
     125              :     }
     126              : 
     127              :     TLV::Tag tag;
     128              :     uint32_t implicitProfileId;
     129              :     ElementTypeContext type;
     130              :     ElementTypeContext subType;
     131              : };
     132              : 
     133              : /*
     134              :  * This templated function inserts a name/value pair into the Json object.
     135              :  * The value is templated to be of type T and accepts any of the following types:
     136              :  *
     137              :  *      bool, uint*_t, int*_t, char *, float, double, std::string, Json::Value
     138              :  *
     139              :  * This method uses the provided element context to generate Json name string.
     140              :  */
     141              : template <typename T>
     142          221 : void InsertJsonElement(Json::Value & json, const JsonObjectElementContext & ctx, T val)
     143              : {
     144          221 :     if (json.isArray())
     145              :     {
     146           69 :         json.append(val);
     147              :     }
     148              :     else
     149              :     {
     150          152 :         json[ctx.GenerateJsonElementName()] = val;
     151              :     }
     152          221 : }
     153              : 
     154              : static CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj);
     155              : 
     156              : /*
     157              :  * Given a TLVReader positioned at TLV structure this function:
     158              :  *   - enters structure
     159              :  *   - converts all elements of a structure into JSON object representation
     160              :  *   - exits structure
     161              :  */
     162           78 : CHIP_ERROR TlvStructToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
     163              : {
     164              :     CHIP_ERROR err;
     165              :     TLV::TLVType containerType;
     166              : 
     167           78 :     ReturnErrorOnFailure(reader.EnterContainer(containerType));
     168              : 
     169          230 :     while ((err = reader.Next()) == CHIP_NO_ERROR)
     170              :     {
     171          155 :         TLV::Tag tag = reader.GetTag();
     172          158 :         VerifyOrReturnError(TLV::IsContextTag(tag) || TLV::IsProfileTag(tag), CHIP_ERROR_INVALID_TLV_TAG);
     173              : 
     174          155 :         if (TLV::IsProfileTag(tag) && TLV::VendorIdFromTag(tag) == 0)
     175              :         {
     176            6 :             VerifyOrReturnError(TLV::TagNumFromTag(tag) > UINT8_MAX, CHIP_ERROR_INVALID_TLV_TAG);
     177              :         }
     178              : 
     179              :         // Recursively convert to JSON the item within the struct.
     180          154 :         ReturnErrorOnFailure(TlvToJson(reader, jsonObj));
     181              :     }
     182              : 
     183           75 :     VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
     184           75 :     return reader.ExitContainer(containerType);
     185              : }
     186              : 
     187          223 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
     188              : {
     189          223 :     JsonObjectElementContext context(reader);
     190              : 
     191          223 :     switch (reader.GetType())
     192              :     {
     193           39 :     case TLV::kTLVType_UnsignedInteger: {
     194              :         uint64_t v;
     195           39 :         ReturnErrorOnFailure(reader.Get(v));
     196           39 :         if (CanCastTo<uint32_t>(v))
     197              :         {
     198           33 :             InsertJsonElement(jsonObj, context, v);
     199              :         }
     200              :         else
     201              :         {
     202            6 :             InsertJsonElement(jsonObj, context, std::to_string(v));
     203              :         }
     204           39 :         break;
     205              :     }
     206              : 
     207           43 :     case TLV::kTLVType_SignedInteger: {
     208              :         int64_t v;
     209           43 :         ReturnErrorOnFailure(reader.Get(v));
     210           43 :         if (CanCastTo<int32_t>(v))
     211              :         {
     212           37 :             InsertJsonElement(jsonObj, context, v);
     213              :         }
     214              :         else
     215              :         {
     216            6 :             InsertJsonElement(jsonObj, context, std::to_string(v));
     217              :         }
     218           43 :         break;
     219              :     }
     220              : 
     221           21 :     case TLV::kTLVType_Boolean: {
     222              :         bool v;
     223           21 :         ReturnErrorOnFailure(reader.Get(v));
     224           21 :         InsertJsonElement(jsonObj, context, v);
     225           21 :         break;
     226              :     }
     227              : 
     228           41 :     case TLV::kTLVType_FloatingPointNumber: {
     229              :         double v;
     230           41 :         ReturnErrorOnFailure(reader.Get(v));
     231           41 :         if (v == std::numeric_limits<double>::infinity())
     232              :         {
     233            3 :             InsertJsonElement(jsonObj, context, kFloatingPointPositiveInfinity);
     234              :         }
     235           38 :         else if (v == -std::numeric_limits<double>::infinity())
     236              :         {
     237            3 :             InsertJsonElement(jsonObj, context, kFloatingPointNegativeInfinity);
     238              :         }
     239              :         else
     240              :         {
     241           35 :             InsertJsonElement(jsonObj, context, v);
     242              :         }
     243           41 :         break;
     244              :     }
     245              : 
     246           14 :     case TLV::kTLVType_ByteString: {
     247           14 :         ByteSpan span;
     248           14 :         ReturnErrorOnFailure(reader.Get(span));
     249              : 
     250           14 :         Platform::ScopedMemoryBuffer<char> byteString;
     251           14 :         byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
     252           14 :         VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
     253              : 
     254           14 :         auto encodedLen              = Base64Encode(span.data(), static_cast<uint16_t>(span.size()), byteString.Get());
     255           14 :         byteString.Get()[encodedLen] = '\0';
     256              : 
     257           14 :         InsertJsonElement(jsonObj, context, byteString.Get());
     258           14 :         break;
     259           14 :     }
     260              : 
     261           17 :     case TLV::kTLVType_UTF8String: {
     262           17 :         CharSpan span;
     263           17 :         ReturnErrorOnFailure(reader.Get(span));
     264              : 
     265           17 :         std::string str(span.data(), span.size());
     266           17 :         InsertJsonElement(jsonObj, context, str);
     267           17 :         break;
     268           17 :     }
     269              : 
     270            6 :     case TLV::kTLVType_Null: {
     271            6 :         InsertJsonElement(jsonObj, context, Json::Value());
     272            6 :         break;
     273              :     }
     274              : 
     275           15 :     case TLV::kTLVType_Structure: {
     276           15 :         Json::Value jsonStruct(Json::objectValue);
     277           15 :         ReturnErrorOnFailure(TlvStructToJson(reader, jsonStruct));
     278           15 :         InsertJsonElement(jsonObj, context, jsonStruct);
     279           15 :         break;
     280           15 :     }
     281              : 
     282           26 :     case TLV::kTLVType_Array: {
     283              :         CHIP_ERROR err;
     284           26 :         Json::Value jsonArray(Json::arrayValue);
     285           26 :         ElementTypeContext prevSubType;
     286           26 :         ElementTypeContext nextSubType;
     287              :         TLV::TLVType containerType;
     288              : 
     289           26 :         ReturnErrorOnFailure(reader.EnterContainer(containerType));
     290              : 
     291           95 :         while ((err = reader.Next()) == CHIP_NO_ERROR)
     292              :         {
     293           70 :             VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
     294           70 :             VerifyOrReturnError(reader.GetType() != TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
     295              : 
     296           70 :             nextSubType.tlvType = reader.GetType();
     297           70 :             if (nextSubType.tlvType == TLV::kTLVType_FloatingPointNumber)
     298              :             {
     299           12 :                 nextSubType.isDouble = reader.IsElementDouble();
     300              :             }
     301              : 
     302           70 :             if (jsonArray.empty())
     303              :             {
     304           21 :                 prevSubType = nextSubType;
     305              :             }
     306              :             else
     307              :             {
     308           49 :                 VerifyOrReturnError(prevSubType.tlvType == nextSubType.tlvType && prevSubType.isDouble == nextSubType.isDouble,
     309              :                                     CHIP_ERROR_INVALID_TLV_ELEMENT);
     310              :             }
     311              : 
     312              :             // Recursively convert to JSON the encompassing item within the array.
     313           69 :             ReturnErrorOnFailure(TlvToJson(reader, jsonArray));
     314              :         }
     315              : 
     316           25 :         VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
     317           25 :         ReturnErrorOnFailure(reader.ExitContainer(containerType));
     318              : 
     319           25 :         context.subType = prevSubType;
     320           25 :         InsertJsonElement(jsonObj, context, jsonArray);
     321           25 :         break;
     322           26 :     }
     323              : 
     324            1 :     default:
     325            1 :         return CHIP_ERROR_INVALID_TLV_ELEMENT;
     326              :         break;
     327              :     }
     328              : 
     329          221 :     return CHIP_NO_ERROR;
     330              : }
     331              : 
     332              : } // namespace
     333              : 
     334           51 : CHIP_ERROR TlvToJson(const ByteSpan & tlv, std::string & jsonString)
     335              : {
     336           51 :     TLV::TLVReader reader;
     337           51 :     reader.Init(tlv);
     338           51 :     reader.ImplicitProfileId = kTemporaryImplicitProfileId;
     339              : 
     340           51 :     ReturnErrorOnFailure(reader.Next());
     341           51 :     return TlvToJson(reader, jsonString);
     342              : }
     343              : 
     344           65 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, std::string & jsonString)
     345              : {
     346              :     // The top level element must be a TLV Structure of Anonymous type.
     347           65 :     VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
     348           64 :     VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
     349              : 
     350              :     // During json conversion, a implicit profile ID is required
     351           63 :     ImplicitProfileIdChange implicitProfileIdChange(reader, kTemporaryImplicitProfileId);
     352              : 
     353           63 :     Json::Value jsonObject(Json::objectValue);
     354           63 :     ReturnErrorOnFailure(TlvStructToJson(reader, jsonObject));
     355              : 
     356           60 :     Json::StyledWriter writer;
     357           60 :     jsonString = writer.write(jsonObject);
     358           60 :     return CHIP_NO_ERROR;
     359           63 : }
     360              : } // namespace chip
        

Generated by: LCOV version 2.0-1