LCOV - code coverage report
Current view: top level - lib/support/jsontlv - TlvJson.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 96 98 98.0 %
Date: 2024-02-15 08:20:41 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 Project CHIP Authors
       4             :  *    Copyright (c) 2013-2017 Nest Labs, Inc.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : #include "lib/support/CHIPMemString.h"
      19             : #include "lib/support/ScopedBuffer.h"
      20             : #include <lib/core/DataModelTypes.h>
      21             : #include <lib/support/Base64.h>
      22             : #include <lib/support/jsontlv/TlvJson.h>
      23             : 
      24             : namespace {
      25             : /*
      26             :  * Encapsulates the different types of keys permissible.
      27             :  *
      28             :  * Root Key = Key with a name of 'value'. This is the top-most key in a given JSON object generated from TLV.
      29             :  * Struct Field = Key containing the 32-bit field ID of an item in a struct.
      30             :  * Array Item = Key containing the 16-bit list index of an item in a list.
      31             :  *
      32             :  * In the latter two modes, the actual field ID/list index is encapsulated within the 'key' member.
      33             :  *
      34             :  */
      35             : struct KeyContext
      36             : {
      37             :     enum KeyType
      38             :     {
      39             :         kRoot,
      40             :         kStructField,
      41             :         kArrayItem
      42             :     };
      43             : 
      44             :     KeyContext() = default;
      45             : 
      46          24 :     KeyContext(chip::FieldId fieldId)
      47          24 :     {
      48          24 :         keyType = kStructField;
      49          24 :         key     = fieldId;
      50          24 :     }
      51             : 
      52           6 :     KeyContext(chip::ListIndex listIndex)
      53           6 :     {
      54           6 :         keyType = kArrayItem;
      55           6 :         key     = listIndex;
      56           6 :     }
      57             : 
      58             :     KeyType keyType  = kRoot;
      59             :     unsigned int key = 0;
      60             : };
      61             : } // namespace
      62             : 
      63             : //
      64             : // For now, let's put a bound of the maximum length of a byte/char string to be the size of an IPv6
      65             : // MTU. While this is smaller than that of the limit defined in the data model specification,
      66             : // strings by virtue of not being chunked are intrinsically limited in size to the size of the encompassing packet.
      67             : //
      68             : static constexpr uint16_t kMaxStringLen = 1280;
      69             : 
      70             : constexpr char kBase64Header[]    = "base64:";
      71             : constexpr size_t kBase64HeaderLen = ArraySize(kBase64Header) - 1;
      72             : 
      73             : namespace chip {
      74             : 
      75             : /*
      76             :  * This templated function inserts a key/value pair into the Json value object.
      77             :  * The value is templated to be of type T and accepts any of the following primitive
      78             :  * types:
      79             :  *      bool, uint*_t, int*_t, char *, float, double.
      80             :  *
      81             :  * This method uses the provided key context to deduce the type of element being added.
      82             :  *
      83             :  */
      84             : template <typename T>
      85          43 : void InsertKeyValue(Json::Value & json, const KeyContext & keyContext, T val)
      86             : {
      87             :     //
      88             :     // This needs to accomodate either the string 'value', or a 32-bit integer.
      89             :     // The size of the largest 32-bit integer key represented as a string is 11 characters long.
      90             :     // Tack on 1 byte for the null character.
      91             :     //
      92             :     char keyBuf[12];
      93             : 
      94          43 :     if (keyContext.keyType == KeyContext::kRoot)
      95             :     {
      96          13 :         Platform::CopyString(keyBuf, sizeof(keyBuf), "value");
      97          13 :         json[keyBuf] = val;
      98             :     }
      99          30 :     else if (keyContext.keyType == KeyContext::kStructField)
     100             :     {
     101          24 :         snprintf(keyBuf, sizeof(keyBuf), "%u", keyContext.key);
     102          24 :         json[keyBuf] = val;
     103             :     }
     104             :     else
     105             :     {
     106           6 :         json[keyContext.key] = val;
     107             :     }
     108          43 : }
     109             : 
     110          13 : std::string JsonToString(Json::Value & json)
     111             : {
     112          13 :     Json::FastWriter writer;
     113          13 :     writer.omitEndingLineFeed();
     114          13 :     return writer.write(json);
     115          13 : }
     116             : 
     117          43 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, KeyContext context, Json::Value & parent)
     118             : {
     119          43 :     switch (reader.GetType())
     120             :     {
     121          14 :     case TLV::kTLVType_UnsignedInteger: {
     122             :         uint64_t v;
     123          14 :         ReturnErrorOnFailure(reader.Get(v));
     124          14 :         InsertKeyValue(parent, context, v);
     125          14 :         break;
     126             :     }
     127             : 
     128           1 :     case TLV::kTLVType_SignedInteger: {
     129             :         int64_t v;
     130           1 :         ReturnErrorOnFailure(reader.Get(v));
     131           1 :         InsertKeyValue(parent, context, v);
     132           1 :         break;
     133             :     }
     134             : 
     135           5 :     case TLV::kTLVType_Boolean: {
     136             :         bool v;
     137           5 :         ReturnErrorOnFailure(reader.Get(v));
     138           5 :         InsertKeyValue(parent, context, v);
     139           5 :         break;
     140             :     }
     141             : 
     142           7 :     case TLV::kTLVType_FloatingPointNumber: {
     143             :         double v;
     144           7 :         ReturnErrorOnFailure(reader.Get(v));
     145           7 :         InsertKeyValue(parent, context, v);
     146           7 :         break;
     147             :     }
     148             : 
     149           4 :     case TLV::kTLVType_ByteString: {
     150           4 :         ByteSpan span;
     151             : 
     152           4 :         ReturnErrorOnFailure(reader.Get(span));
     153           4 :         VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
     154             : 
     155           4 :         Platform::ScopedMemoryBuffer<char> byteString;
     156           4 :         byteString.Alloc(kBase64HeaderLen + BASE64_ENCODED_LEN(span.size()) + 1);
     157           4 :         VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
     158             : 
     159           4 :         auto encodedLen = Base64Encode(span.data(), static_cast<uint16_t>(span.size()), byteString.Get() + kBase64HeaderLen);
     160           4 :         if (encodedLen)
     161             :         {
     162           4 :             memcpy(byteString.Get(), kBase64Header, kBase64HeaderLen);
     163           4 :             encodedLen = static_cast<uint16_t>(encodedLen + kBase64HeaderLen);
     164             :         }
     165           4 :         byteString.Get()[encodedLen] = '\0';
     166             : 
     167           4 :         InsertKeyValue(parent, context, byteString.Get());
     168           4 :         break;
     169           4 :     }
     170             : 
     171           4 :     case TLV::kTLVType_UTF8String: {
     172           4 :         CharSpan span;
     173             : 
     174           4 :         ReturnErrorOnFailure(reader.Get(span));
     175           4 :         VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
     176             : 
     177           4 :         Platform::ScopedMemoryString charString(span.data(), span.size());
     178           4 :         InsertKeyValue(parent, context, charString.Get());
     179           4 :         break;
     180           4 :     }
     181             : 
     182           2 :     case TLV::kTLVType_Null: {
     183           2 :         InsertKeyValue(parent, context, Json::Value());
     184           2 :         break;
     185             :     }
     186             : 
     187           3 :     case TLV::kTLVType_Structure: {
     188             :         TLV::TLVType containerType;
     189           3 :         ReturnErrorOnFailure(reader.EnterContainer(containerType));
     190             : 
     191             :         CHIP_ERROR err;
     192           3 :         Json::Value value;
     193             : 
     194          27 :         while ((err = reader.Next()) == CHIP_NO_ERROR)
     195             :         {
     196          24 :             VerifyOrReturnError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG);
     197          24 :             KeyContext context2(static_cast<chip::FieldId>(TLV::TagNumFromTag(reader.GetTag())));
     198             : 
     199             :             //
     200             :             // Recursively convert to JSON the encompassing item within the struct.
     201             :             //
     202          24 :             ReturnErrorOnFailure(TlvToJson(reader, context2, value));
     203             :         }
     204             : 
     205           3 :         VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
     206           3 :         ReturnErrorOnFailure(reader.ExitContainer(containerType));
     207           3 :         InsertKeyValue(parent, context, value);
     208           3 :         break;
     209           3 :     }
     210             : 
     211           3 :     case TLV::kTLVType_Array: {
     212             :         TLV::TLVType containerType;
     213           3 :         ReturnErrorOnFailure(reader.EnterContainer(containerType));
     214             : 
     215             :         CHIP_ERROR err;
     216           3 :         Json::Value value = Json::Value(Json::arrayValue);
     217           3 :         size_t listIndex  = 0;
     218             : 
     219           9 :         while ((err = reader.Next()) == CHIP_NO_ERROR)
     220             :         {
     221           6 :             KeyContext context2(static_cast<chip::ListIndex>(listIndex++));
     222             : 
     223             :             //
     224             :             // Recursively convert to JSON the encompassing item within the array.
     225             :             //
     226           6 :             ReturnErrorOnFailure(TlvToJson(reader, context2, value));
     227             :         }
     228             : 
     229           3 :         VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
     230           3 :         ReturnErrorOnFailure(reader.ExitContainer(containerType));
     231           3 :         InsertKeyValue(parent, context, value);
     232           3 :         break;
     233           3 :     }
     234             : 
     235           0 :     default:
     236           0 :         return CHIP_ERROR_INVALID_TLV_ELEMENT;
     237             :         break;
     238             :     }
     239             : 
     240          43 :     return CHIP_NO_ERROR;
     241             : }
     242             : 
     243          13 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & root)
     244             : {
     245          13 :     KeyContext context;
     246          13 :     return TlvToJson(reader, context, root);
     247             : }
     248             : 
     249             : } // namespace chip

Generated by: LCOV version 1.14