blob: 3cf3cdb2183780531346c03fed148868e88f91c7 [file] [log] [blame]
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
#pragma once
#include <lib/core/TLVReader.h>
#include <lib/format/FlatTreePosition.h>
#include <lib/format/tlv_meta.h>
#include <lib/support/StringBuilder.h>
#include <protocols/Protocols.h>
namespace chip {
namespace Decoders {
/// Represents an individual decoded entry for IM Payloads
/// Generally a name + value + metadata tuple, where name and value are never NULL.
class PayloadEntry
{
public:
static constexpr uint32_t kInvalidId = 0xFFFFFFFF;
enum class IMPayloadType
{
kNone = 0, // generally should not be used except initial init
kValue, // represents a simple value to output
kNestingEnter, // Nested struct enter. Has name, empty value
kNestingExit, // Nested struct exit (has no name/value)
// Content payloads
kAttribute,
kCommand,
kEvent,
};
PayloadEntry(const PayloadEntry &) = default;
PayloadEntry & operator=(const PayloadEntry &) = default;
PayloadEntry() : mType(IMPayloadType::kNone), mName(""), mValue("") {}
IMPayloadType GetType() const { return mType; }
const char * GetName() const { return mName; }
const char * GetValueText() const { return mValue; }
/// valid only if payload is an IM Payload
uint32_t GetClusterId() const { return mClusterId; };
/// valid only if payload as an Attribute ID
uint32_t GetAttributeId() const
{
VerifyOrReturnValue(mType == IMPayloadType::kAttribute, kInvalidId);
return mSubId;
}
/// valid only if payload as a Command ID
uint32_t GetCommandId() const
{
VerifyOrReturnValue(mType == IMPayloadType::kCommand, kInvalidId);
return mSubId;
}
/// valid only if payload as a Command ID
uint32_t GetEventId() const
{
VerifyOrReturnValue(mType == IMPayloadType::kEvent, kInvalidId);
return mSubId;
}
static PayloadEntry SimpleValue(const char * name, const char * value)
{
return PayloadEntry(IMPayloadType::kValue, name, value);
}
static PayloadEntry NestingEnter(const char * name) { return PayloadEntry(IMPayloadType::kNestingEnter, name, ""); }
static PayloadEntry NestingExit() { return PayloadEntry(IMPayloadType::kNestingExit, "", ""); }
static PayloadEntry AttributePayload(uint32_t cluster_id, uint32_t attribute_id)
{
PayloadEntry result(IMPayloadType::kAttribute, "ATTR_DATA", "");
result.mClusterId = cluster_id;
result.mSubId = attribute_id;
return result;
}
static PayloadEntry CommandPayload(uint32_t cluster_id, uint32_t command_id)
{
PayloadEntry result(IMPayloadType::kCommand, "COMMAND_DATA", "");
result.mClusterId = cluster_id;
result.mSubId = command_id;
return result;
}
static PayloadEntry EventPayload(uint32_t cluster_id, uint32_t event_id)
{
PayloadEntry result(IMPayloadType::kEvent, "EVENT_DATA", "");
result.mClusterId = cluster_id;
result.mSubId = event_id;
return result;
}
private:
PayloadEntry(IMPayloadType type, const char * name, const char * value) : mType(type), mName(name), mValue(value) {}
IMPayloadType mType = IMPayloadType::kValue;
const char * mName = nullptr;
const char * mValue = nullptr;
uint32_t mClusterId = 0;
uint32_t mSubId = 0; // attribute, command or event id
};
/// Sets up decoding of some Matter data payload
class PayloadDecoderInitParams
{
public:
using DecodeTree = const FlatTree::Node<chip::TLVMeta::ItemInfo> *;
PayloadDecoderInitParams() = default;
PayloadDecoderInitParams & SetProtocol(Protocols::Id value)
{
mProtocol = value;
return *this;
}
PayloadDecoderInitParams & SetMessageType(uint8_t value)
{
mMessageType = value;
return *this;
}
PayloadDecoderInitParams & SetProtocolDecodeTree(DecodeTree tree, size_t s)
{
mProtocolTree = tree;
mProtocolTreeSize = s;
return *this;
}
template <size_t N>
PayloadDecoderInitParams & SetProtocolDecodeTree(const std::array<const FlatTree::Node<chip::TLVMeta::ItemInfo>, N> & a)
{
return SetProtocolDecodeTree(a.data(), N);
}
PayloadDecoderInitParams & SetClusterDecodeTree(DecodeTree tree, size_t s)
{
mClusterTree = tree;
mClusterTreeSize = s;
return *this;
}
template <size_t N>
PayloadDecoderInitParams & SetClusterDecodeTree(const std::array<const FlatTree::Node<chip::TLVMeta::ItemInfo>, N> & a)
{
return SetClusterDecodeTree(a.data(), N);
}
DecodeTree GetProtocolDecodeTree() const { return mProtocolTree; }
size_t GetProtocolDecodeTreeSize() const { return mProtocolTreeSize; }
DecodeTree GetClusterDecodeTree() const { return mClusterTree; }
size_t GetClusterDecodeTreeSize() const { return mClusterTreeSize; }
Protocols::Id GetProtocol() const { return mProtocol; }
uint8_t GetMessageType() const { return mMessageType; }
private:
DecodeTree mProtocolTree = nullptr;
size_t mProtocolTreeSize = 0;
DecodeTree mClusterTree = nullptr;
size_t mClusterTreeSize = 0;
Protocols::Id mProtocol = Protocols::NotSpecified;
uint8_t mMessageType = 0;
};
class PayloadDecoderBase
{
public:
static constexpr size_t kMaxDecodeDepth = 16;
using DecodePosition = chip::FlatTree::Position<chip::TLVMeta::ItemInfo, kMaxDecodeDepth>;
PayloadDecoderBase(const PayloadDecoderInitParams & params, StringBuilderBase & nameBuilder, StringBuilderBase & valueBuilder);
/// Initialize decoding from the given reader
/// Will create a copy of the reader, however the copy will contain
/// pointers in the original reader (so data must stay valid while Next is called)
void StartDecoding(const TLV::TLVReader & reader);
void StartDecoding(chip::ByteSpan data)
{
TLV::TLVReader reader;
reader.Init(data);
StartDecoding(reader);
}
/// Get the next decoded entry from the underlying TLV
///
/// If a cluster decoder is set, then kAttribute/kCommand/kEvent are ALWAYS decoded
/// (even if unknown tags), otherwise they will be returned as separate PayloadEntry values.
///
/// Returns false when decoding finished.
bool Next(PayloadEntry & entry);
const TLV::TLVReader & ReadState() const { return mReader; }
private:
enum class State
{
kStarting,
kValueRead, // reading value for Payload
kContentRead, // reading value for IMContent (i.e. decoded attr/cmd/event)
kDone,
};
/// Move to the given attribute/event/command entry.
///
/// [entry] MUST be of type command/attribute/event.
///
/// This call either moves to "ContentDecoding mode" if content tree is available
/// or leaves entry unchanged if content decoding tree is not available.
void MoveToContent(PayloadEntry & entry);
void NextFromStarting(PayloadEntry & entry);
void NextFromValueRead(PayloadEntry & entry);
void NextFromContentRead(PayloadEntry & entry);
/// Enter the container in mReader.
///
/// May change entry/state in case of errors.
///
/// Returns false on error (and entry is changed in that case)
bool ReaderEnterContainer(PayloadEntry & entry);
/// Returns a "nesting enter" value, making sure that
/// nesting depth is sufficient.
void EnterContainer(PayloadEntry & entry);
/// Returns a "nesting exit" value, making sure that
/// nesting depth is sufficient.
void ExitContainer(PayloadEntry & entry);
const chip::Protocols::Id mProtocol;
const uint8_t mMessageType;
StringBuilderBase & mNameBuilder;
StringBuilderBase & mValueBuilder;
State mState = State::kStarting;
TLV::TLVReader mReader;
DecodePosition mPayloadPosition;
DecodePosition mIMContentPosition;
TLV::TLVType mNestingEnters[kMaxDecodeDepth];
size_t mCurrentNesting = 0;
/// incremental state for parsing of paths
uint32_t mClusterId = 0;
uint32_t mAttributeId = 0;
uint32_t mEventId = 0;
uint32_t mCommandId = 0;
};
template <size_t kNameBufferSize, size_t kValueBufferSize>
class PayloadDecoder : public PayloadDecoderBase
{
public:
PayloadDecoder(const PayloadDecoderInitParams & params) : PayloadDecoderBase(std::move(params), mName, mValue) {}
private:
chip::StringBuilder<kNameBufferSize> mName;
chip::StringBuilder<kValueBufferSize> mValue;
};
} // namespace Decoders
} // namespace chip