LCOV - code coverage report
Current view: top level - lib/support/jsontlv - TlvToJson.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 154 155 99.4 %
Date: 2024-02-15 08:20:41 Functions: 17 17 100.0 %

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

Generated by: LCOV version 1.14