blob: e7044da118f0b041eebcf09b5b3d8ff68cdaffd7 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
*
* 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.
*/
#include "lib/support/CHIPMemString.h"
#include "lib/support/ScopedBuffer.h"
#include <lib/core/DataModelTypes.h>
#include <lib/support/Base64.h>
#include <lib/support/jsontlv/TlvJson.h>
namespace {
/*
* Encapsulates the different types of keys permissible.
*
* Root Key = Key with a name of 'value'. This is the top-most key in a given JSON object generated from TLV.
* Struct Field = Key containing the 32-bit field ID of an item in a struct.
* Array Item = Key containing the 16-bit list index of an item in a list.
*
* In the latter two modes, the actual field ID/list index is encapsulated within the 'key' member.
*
*/
struct KeyContext
{
enum KeyType
{
kRoot,
kStructField,
kArrayItem
};
KeyContext() = default;
KeyContext(chip::FieldId fieldId)
{
keyType = kStructField;
key = fieldId;
}
KeyContext(chip::ListIndex listIndex)
{
keyType = kArrayItem;
key = listIndex;
}
KeyType keyType = kRoot;
unsigned int key = 0;
};
} // namespace
//
// For now, let's put a bound of the maximum length of a byte/char string to be the size of an IPv6
// MTU. While this is smaller than that of the limit defined in the data model specification,
// strings by virtue of not being chunked are intrinsically limited in size to the size of the encompassing packet.
//
static constexpr uint16_t kMaxStringLen = 1280;
namespace chip {
/*
* This templated function inserts a key/value pair into the Json value object.
* The value is templated to be of type T and accepts any of the following primitive
* types:
* bool, uint*_t, int*_t, char *, float, double.
*
* This method uses the provided key context to deduce the type of element being added.
*
*/
template <typename T>
void InsertKeyValue(Json::Value & json, const KeyContext & keyContext, T val)
{
//
// This needs to accomodate either the string 'value', or a 32-bit integer.
// The size of the largest 32-bit integer key represented as a string is 11 characters long.
// Tack on 1 byte for the null character.
//
char keyBuf[12];
if (keyContext.keyType == KeyContext::kRoot)
{
Platform::CopyString(keyBuf, sizeof(keyBuf), "value");
json[keyBuf] = val;
}
else if (keyContext.keyType == KeyContext::kStructField)
{
snprintf(keyBuf, sizeof(keyBuf), "%u", keyContext.key);
json[keyBuf] = val;
}
else
{
json[keyContext.key] = val;
}
}
std::string JsonToString(Json::Value & json)
{
Json::FastWriter writer;
writer.omitEndingLineFeed();
return writer.write(json);
}
CHIP_ERROR TlvToJson(TLV::TLVReader & reader, KeyContext context, Json::Value & parent)
{
bool isStruct = false;
switch (reader.GetType())
{
case TLV::kTLVType_UnsignedInteger: {
uint64_t v;
ReturnErrorOnFailure(reader.Get(v));
InsertKeyValue(parent, context, v);
break;
}
case TLV::kTLVType_SignedInteger: {
int64_t v;
ReturnErrorOnFailure(reader.Get(v));
InsertKeyValue(parent, context, v);
break;
}
case TLV::kTLVType_Boolean: {
bool v;
ReturnErrorOnFailure(reader.Get(v));
InsertKeyValue(parent, context, v);
break;
}
case TLV::kTLVType_FloatingPointNumber: {
double v;
ReturnErrorOnFailure(reader.Get(v));
InsertKeyValue(parent, context, v);
break;
}
case TLV::kTLVType_ByteString: {
ByteSpan span;
ReturnErrorOnFailure(reader.Get(span));
VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
Platform::ScopedMemoryBuffer<char> byteString;
byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
auto encodedLen = Base64Encode(span.data(), span.size(), byteString.Get());
byteString.Get()[encodedLen] = '\0';
InsertKeyValue(parent, context, byteString.Get());
break;
}
case TLV::kTLVType_UTF8String: {
CharSpan span;
ReturnErrorOnFailure(reader.Get(span));
VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
Platform::ScopedMemoryString charString(span.data(), span.size());
InsertKeyValue(parent, context, charString.Get());
break;
}
case TLV::kTLVType_Null: {
InsertKeyValue(parent, context, Json::Value());
break;
}
case TLV::kTLVType_Structure:
isStruct = true;
//
// Fall-through to the case below since
// arrays and structs are handled similarly with
// just a small difference in terms of handling of field IDs vs.
// list indices of the elements in the respective collections.
//
case TLV::kTLVType_Array: {
TLV::TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
CHIP_ERROR err;
Json::Value value;
size_t listIndex = 0;
while ((err = reader.Next()) == CHIP_NO_ERROR)
{
if (isStruct)
{
VerifyOrReturnError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG);
KeyContext context2(static_cast<chip::FieldId>(TLV::TagNumFromTag(reader.GetTag())));
//
// Recursively convert to JSON the encompassing item within the struct.
//
ReturnErrorOnFailure(TlvToJson(reader, context2, value));
}
else
{
KeyContext context2(static_cast<chip::ListIndex>(listIndex++));
//
// Recursively convert to JSON the encompassing item within the array.
//
ReturnErrorOnFailure(TlvToJson(reader, context2, value));
}
}
VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
ReturnErrorOnFailure(reader.ExitContainer(containerType));
InsertKeyValue(parent, context, value);
break;
}
default:
return CHIP_ERROR_INVALID_TLV_ELEMENT;
break;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & root)
{
KeyContext context;
return TlvToJson(reader, context, root);
}
} // namespace chip