Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2023 Project CHIP Authors
4 : * All rights reserved.
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 : #pragma once
19 :
20 : #include <lib/core/TLVReader.h>
21 : #include <lib/format/FlatTreePosition.h>
22 : #include <lib/format/tlv_meta.h>
23 : #include <lib/support/StringBuilder.h>
24 : #include <protocols/Protocols.h>
25 :
26 : namespace chip {
27 : namespace Decoders {
28 :
29 : /// Represents an individual decoded entry for IM Payloads
30 : /// Generally a name + value + metadata tuple, where name and value are never NULL.
31 : class PayloadEntry
32 : {
33 : public:
34 : static constexpr uint32_t kInvalidId = 0xFFFFFFFF;
35 : enum class IMPayloadType
36 : {
37 : kNone = 0, // generally should not be used except initial init
38 : kValue, // represents a simple value to output
39 :
40 : kNestingEnter, // Nested struct enter. Has name, empty value
41 : kNestingExit, // Nested struct exit (has no name/value)
42 :
43 : // Content payloads
44 : kAttribute,
45 : kCommand,
46 : kEvent,
47 : };
48 : PayloadEntry(const PayloadEntry &) = default;
49 : PayloadEntry & operator=(const PayloadEntry &) = default;
50 :
51 : PayloadEntry() : mType(IMPayloadType::kNone), mName(""), mValue("") {}
52 :
53 1015 : IMPayloadType GetType() const { return mType; }
54 :
55 : const char * GetName() const { return mName; }
56 : const char * GetValueText() const { return mValue; }
57 :
58 : /// valid only if payload is an IM Payload
59 25 : uint32_t GetClusterId() const { return mClusterId; };
60 :
61 : /// valid only if payload as an Attribute ID
62 31 : uint32_t GetAttributeId() const
63 : {
64 31 : VerifyOrReturnValue(mType == IMPayloadType::kAttribute, kInvalidId);
65 31 : return mSubId;
66 : }
67 :
68 : /// valid only if payload as a Command ID
69 4 : uint32_t GetCommandId() const
70 : {
71 4 : VerifyOrReturnValue(mType == IMPayloadType::kCommand, kInvalidId);
72 4 : return mSubId;
73 : }
74 :
75 : /// valid only if payload as a Command ID
76 4 : uint32_t GetEventId() const
77 : {
78 4 : VerifyOrReturnValue(mType == IMPayloadType::kEvent, kInvalidId);
79 4 : return mSubId;
80 : }
81 :
82 236 : static PayloadEntry SimpleValue(const char * name, const char * value)
83 : {
84 236 : return PayloadEntry(IMPayloadType::kValue, name, value);
85 : }
86 :
87 166 : static PayloadEntry NestingEnter(const char * name) { return PayloadEntry(IMPayloadType::kNestingEnter, name, ""); }
88 :
89 166 : static PayloadEntry NestingExit() { return PayloadEntry(IMPayloadType::kNestingExit, "", ""); }
90 :
91 20 : static PayloadEntry AttributePayload(uint32_t cluster_id, uint32_t attribute_id)
92 : {
93 20 : PayloadEntry result(IMPayloadType::kAttribute, "ATTR_DATA", "");
94 20 : result.mClusterId = cluster_id;
95 20 : result.mSubId = attribute_id;
96 :
97 20 : return result;
98 : }
99 :
100 2 : static PayloadEntry CommandPayload(uint32_t cluster_id, uint32_t command_id)
101 : {
102 2 : PayloadEntry result(IMPayloadType::kCommand, "COMMAND_DATA", "");
103 2 : result.mClusterId = cluster_id;
104 2 : result.mSubId = command_id;
105 2 : return result;
106 : }
107 :
108 2 : static PayloadEntry EventPayload(uint32_t cluster_id, uint32_t event_id)
109 : {
110 2 : PayloadEntry result(IMPayloadType::kEvent, "EVENT_DATA", "");
111 2 : result.mClusterId = cluster_id;
112 2 : result.mSubId = event_id;
113 2 : return result;
114 : }
115 :
116 : private:
117 592 : PayloadEntry(IMPayloadType type, const char * name, const char * value) : mType(type), mName(name), mValue(value) {}
118 :
119 : IMPayloadType mType = IMPayloadType::kValue;
120 : const char * mName = nullptr;
121 : const char * mValue = nullptr;
122 :
123 : uint32_t mClusterId = 0;
124 : uint32_t mSubId = 0; // attribute, command or event id
125 : };
126 :
127 : /// Sets up decoding of some Matter data payload
128 : class PayloadDecoderInitParams
129 : {
130 : public:
131 : using DecodeTree = const FlatTree::Node<chip::TLVMeta::ItemInfo> *;
132 : PayloadDecoderInitParams() = default;
133 :
134 : PayloadDecoderInitParams & SetProtocol(Protocols::Id value)
135 : {
136 : mProtocol = value;
137 : return *this;
138 : }
139 :
140 : PayloadDecoderInitParams & SetMessageType(uint8_t value)
141 : {
142 : mMessageType = value;
143 : return *this;
144 : }
145 :
146 : PayloadDecoderInitParams & SetProtocolDecodeTree(DecodeTree tree, size_t s)
147 : {
148 : mProtocolTree = tree;
149 : mProtocolTreeSize = s;
150 : return *this;
151 : }
152 :
153 : template <size_t N>
154 : PayloadDecoderInitParams & SetProtocolDecodeTree(const std::array<const FlatTree::Node<chip::TLVMeta::ItemInfo>, N> & a)
155 : {
156 : return SetProtocolDecodeTree(a.data(), N);
157 : }
158 :
159 : PayloadDecoderInitParams & SetClusterDecodeTree(DecodeTree tree, size_t s)
160 : {
161 : mClusterTree = tree;
162 : mClusterTreeSize = s;
163 : return *this;
164 : }
165 :
166 : template <size_t N>
167 : PayloadDecoderInitParams & SetClusterDecodeTree(const std::array<const FlatTree::Node<chip::TLVMeta::ItemInfo>, N> & a)
168 : {
169 : return SetClusterDecodeTree(a.data(), N);
170 : }
171 :
172 28 : DecodeTree GetProtocolDecodeTree() const { return mProtocolTree; }
173 28 : size_t GetProtocolDecodeTreeSize() const { return mProtocolTreeSize; }
174 28 : DecodeTree GetClusterDecodeTree() const { return mClusterTree; }
175 28 : size_t GetClusterDecodeTreeSize() const { return mClusterTreeSize; }
176 :
177 28 : Protocols::Id GetProtocol() const { return mProtocol; }
178 28 : uint8_t GetMessageType() const { return mMessageType; }
179 :
180 : private:
181 : DecodeTree mProtocolTree = nullptr;
182 : size_t mProtocolTreeSize = 0;
183 :
184 : DecodeTree mClusterTree = nullptr;
185 : size_t mClusterTreeSize = 0;
186 :
187 : Protocols::Id mProtocol = Protocols::NotSpecified;
188 : uint8_t mMessageType = 0;
189 : };
190 :
191 : class PayloadDecoderBase
192 : {
193 : public:
194 : static constexpr size_t kMaxDecodeDepth = 16;
195 : using DecodePosition = chip::FlatTree::Position<chip::TLVMeta::ItemInfo, kMaxDecodeDepth>;
196 :
197 : PayloadDecoderBase(const PayloadDecoderInitParams & params, StringBuilderBase & nameBuilder, StringBuilderBase & valueBuilder);
198 :
199 : /// Initialize decoding from the given reader
200 : /// Will create a copy of the reader, however the copy will contain
201 : /// pointers in the original reader (so data must stay valid while Next is called)
202 : void StartDecoding(const TLV::TLVReader & reader);
203 :
204 : void StartDecoding(chip::ByteSpan data)
205 : {
206 : TLV::TLVReader reader;
207 : reader.Init(data);
208 : StartDecoding(reader);
209 : }
210 :
211 : /// Get the next decoded entry from the underlying TLV
212 : ///
213 : /// If a cluster decoder is set, then kAttribute/kCommand/kEvent are ALWAYS decoded
214 : /// (even if unknown tags), otherwise they will be returned as separate PayloadEntry values.
215 : ///
216 : /// Returns false when decoding finished.
217 : bool Next(PayloadEntry & entry);
218 :
219 : const TLV::TLVReader & ReadState() const { return mReader; }
220 :
221 : private:
222 : enum class State
223 : {
224 : kStarting,
225 : kValueRead, // reading value for Payload
226 : kContentRead, // reading value for IMContent (i.e. decoded attr/cmd/event)
227 : kDone,
228 : };
229 :
230 : /// Move to the given attribute/event/command entry.
231 : ///
232 : /// [entry] MUST be of type command/attribute/event.
233 : ///
234 : /// This call either moves to "ContentDecoding mode" if content tree is available
235 : /// or leaves entry unchanged if content decoding tree is not available.
236 : void MoveToContent(PayloadEntry & entry);
237 :
238 : void NextFromStarting(PayloadEntry & entry);
239 : void NextFromValueRead(PayloadEntry & entry);
240 : void NextFromContentRead(PayloadEntry & entry);
241 :
242 : /// Enter the container in mReader.
243 : ///
244 : /// May change entry/state in case of errors.
245 : ///
246 : /// Returns false on error (and entry is changed in that case)
247 : bool ReaderEnterContainer(PayloadEntry & entry);
248 :
249 : /// Returns a "nesting enter" value, making sure that
250 : /// nesting depth is sufficient.
251 : void EnterContainer(PayloadEntry & entry);
252 :
253 : /// Returns a "nesting exit" value, making sure that
254 : /// nesting depth is sufficient.
255 : void ExitContainer(PayloadEntry & entry);
256 :
257 : const chip::Protocols::Id mProtocol;
258 : const uint8_t mMessageType;
259 :
260 : StringBuilderBase & mNameBuilder;
261 : StringBuilderBase & mValueBuilder;
262 :
263 : State mState = State::kStarting;
264 : TLV::TLVReader mReader;
265 : DecodePosition mPayloadPosition;
266 : DecodePosition mIMContentPosition;
267 : TLV::TLVType mNestingEnters[kMaxDecodeDepth];
268 : size_t mCurrentNesting = 0;
269 :
270 : /// incremental state for parsing of paths
271 : uint32_t mClusterId = 0;
272 : uint32_t mAttributeId = 0;
273 : uint32_t mEventId = 0;
274 : uint32_t mCommandId = 0;
275 : };
276 :
277 : template <size_t kNameBufferSize, size_t kValueBufferSize>
278 : class PayloadDecoder : public PayloadDecoderBase
279 : {
280 : public:
281 : PayloadDecoder(const PayloadDecoderInitParams & params) : PayloadDecoderBase(std::move(params), mName, mValue) {}
282 :
283 : private:
284 : chip::StringBuilder<kNameBufferSize> mName;
285 : chip::StringBuilder<kValueBufferSize> mValue;
286 : };
287 :
288 : } // namespace Decoders
289 : } // namespace chip
|