Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * Copyright (c) 2015-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 :
19 : /**
20 : * @file
21 : * This file implements utility interfaces for managing and
22 : * working with CHIP TLV.
23 : *
24 : */
25 :
26 : #include <lib/core/TLVUtilities.h>
27 :
28 : #include <lib/core/CHIPError.h>
29 : #include <lib/core/TLVReader.h>
30 : #include <lib/core/TLVTags.h>
31 : #include <lib/core/TLVTypes.h>
32 : #include <lib/support/CodeUtils.h>
33 :
34 : namespace chip {
35 :
36 : namespace TLV {
37 :
38 : namespace Utilities {
39 :
40 : namespace {
41 :
42 : // Sets up a limit on recursion depth, to avoid any stack overflows
43 : // on very deep TLV structures. Embedded has limited stack space.
44 : constexpr size_t kMaxRecursionDepth = 10;
45 :
46 : } // namespace
47 :
48 : struct FindContext
49 : {
50 : const Tag & mTag;
51 : TLVReader & mReader;
52 : };
53 :
54 : /**
55 : * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler
56 : * for each visited TLV element in the context of @a aContext.
57 : * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR
58 : *
59 : * @param[in] aReader A reference to the TLV reader containing the TLV
60 : * data to iterate.
61 : * @param[in] aDepth The current depth into the TLV data.
62 : * @param[in] aHandler A callback to invoke for the current TLV element
63 : * being visited.
64 : * @param[in,out] aContext An optional pointer to caller-provided context data.
65 : * @param[in] aRecurse A Boolean indicating whether (true) or not (false)
66 : * any encountered arrays or structures should be
67 : * descended into.
68 : *
69 : * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding,
70 : * or to the end of a TLV container.
71 : *
72 : * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR
73 : */
74 9075 : static CHIP_ERROR Iterate(TLVReader & aReader, size_t aDepth, IterateHandler aHandler, void * aContext, bool aRecurse)
75 : {
76 9075 : CHIP_ERROR retval = CHIP_NO_ERROR;
77 :
78 9075 : if (aDepth >= kMaxRecursionDepth)
79 : {
80 0 : return CHIP_ERROR_RECURSION_DEPTH_LIMIT;
81 : }
82 :
83 9075 : if (aReader.GetType() == kTLVType_NotSpecified)
84 : {
85 9030 : ReturnErrorOnFailure(aReader.Next());
86 : }
87 :
88 : do
89 : {
90 39060 : const TLVType theType = aReader.GetType();
91 :
92 39060 : ReturnErrorOnFailure((aHandler) (aReader, aDepth, aContext));
93 :
94 38073 : if (aRecurse && TLVTypeIsContainer(theType))
95 : {
96 : TLVType containerType;
97 :
98 529 : ReturnErrorOnFailure(aReader.EnterContainer(containerType));
99 :
100 529 : retval = Iterate(aReader, aDepth + 1, aHandler, aContext, aRecurse);
101 529 : if ((retval != CHIP_END_OF_TLV) && (retval != CHIP_NO_ERROR))
102 : {
103 4 : return retval;
104 : }
105 :
106 525 : ReturnErrorOnFailure(aReader.ExitContainer(containerType));
107 : }
108 38067 : } while ((retval = aReader.Next()) == CHIP_NO_ERROR);
109 :
110 8065 : return retval;
111 : }
112 :
113 : /**
114 : * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler
115 : * for each visited TLV element in the context of @a aContext.
116 : * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR
117 : *
118 : * @param[in] aReader A reference to the TLV reader containing the TLV
119 : * data to iterate.
120 : * @param[in] aHandler A callback to invoke for the current TLV element
121 : * being visited.
122 : * @param[in,out] aContext An optional pointer to caller-provided context data.
123 : *
124 : * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding,
125 : * or to the end of a TLV container.
126 : *
127 : * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aHandler is NULL.
128 : *
129 : * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR
130 : *
131 : */
132 76 : CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext)
133 : {
134 76 : const bool recurse = true;
135 : CHIP_ERROR retval;
136 :
137 76 : retval = Iterate(aReader, aHandler, aContext, recurse);
138 :
139 76 : return retval;
140 : }
141 :
142 : /**
143 : * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler
144 : * for each visited TLV element in the context of @a aContext.
145 : * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR
146 : *
147 : * @param[in] aReader A reference to the TLV reader containing the TLV
148 : * data to iterate.
149 : * @param[in] aHandler A callback to invoke for the current TLV element
150 : * being visited.
151 : * @param[in,out] aContext An optional pointer to caller-provided context data.
152 : * @param[in] aRecurse A Boolean indicating whether (true) or not (false)
153 : * any encountered arrays or structures should be
154 : * descended into.
155 : *
156 : * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding,
157 : * or to the end of a TLV container.
158 : *
159 : * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aHandler is NULL.
160 : *
161 : * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR
162 : *
163 : */
164 8546 : CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext, const bool aRecurse)
165 : {
166 8546 : VerifyOrReturnError(aHandler != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
167 :
168 : TLVReader temp;
169 8546 : temp.Init(aReader);
170 :
171 8546 : constexpr size_t depth = 0;
172 8546 : return Iterate(temp, depth, aHandler, aContext, aRecurse);
173 : }
174 :
175 : /**
176 : * Increment the counter when iterating through the TLV data.
177 : *
178 : * @param[in] aReader A reference to the TLV reader containing the TLV
179 : * data to count the number of TLV elements.
180 : * @param[in] aDepth The current depth into the TLV data.
181 : * @param[in,out] aContext A pointer to the handler-specific context which
182 : * is a pointer to storage for the count value.
183 : *
184 : * @retval #CHIP_NO_ERROR On success.
185 : *
186 : * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aContext is NULL.
187 : *
188 : */
189 2435 : static CHIP_ERROR CountHandler(const TLVReader & aReader, size_t aDepth, void * aContext)
190 : {
191 2435 : VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
192 :
193 2435 : *static_cast<size_t *>(aContext) += 1;
194 :
195 2435 : return CHIP_NO_ERROR;
196 : }
197 :
198 : /**
199 : * Count the number of TLV elements within the specified TLV reader,
200 : * descending into arrays or structures.
201 : *
202 : * @param[in] aReader A read-only reference to the TLV reader for
203 : * which to count the number of TLV elements.
204 : * @param[in,out] aCount A reference to storage for the returned count.
205 : * This is initialized to zero (0) prior to counting
206 : * and is set to the number of elements counted on
207 : * success.
208 : *
209 : * @retval #CHIP_NO_ERROR On success.
210 : *
211 : */
212 2 : CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount)
213 : {
214 2 : constexpr bool recurse = true;
215 2 : return Count(aReader, aCount, recurse);
216 : }
217 :
218 : /**
219 : * Count the number of TLV elements within the specified TLV reader,
220 : * optionally descending into arrays or structures.
221 : *
222 : * @param[in] aReader A read-only reference to the TLV reader for
223 : * which to count the number of TLV elements.
224 : * @param[in,out] aCount A reference to storage for the returned count.
225 : * This is initialized to zero (0) prior to counting
226 : * and is set to the number of elements counted on
227 : * success.
228 : * @param[in] aRecurse A Boolean indicating whether (true) or not (false)
229 : * any encountered arrays or structures should be
230 : * descended into.
231 : *
232 : * @retval #CHIP_NO_ERROR On success.
233 : *
234 : */
235 1094 : CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount, const bool aRecurse)
236 : {
237 1094 : aCount = 0;
238 :
239 1094 : CHIP_ERROR retval = Iterate(aReader, CountHandler, &aCount, aRecurse);
240 :
241 1094 : if (retval == CHIP_END_OF_TLV)
242 1094 : retval = CHIP_NO_ERROR;
243 :
244 1094 : return retval;
245 : }
246 :
247 : /**
248 : * Search for the specified tag within the provided TLV reader.
249 : *
250 : * @param[in] aReader A read-only reference to the TLV reader in
251 : * which to find the specified tag.
252 : * @param[in] aDepth The current depth into the TLV data.
253 : * @param[in,out] aContext A pointer to the handler-specific context.
254 : *
255 : * @retval #CHIP_NO_ERROR On success.
256 : *
257 : * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aContext is NULL.
258 : *
259 : * @retval #CHIP_ERROR_SENTINEL If the specified tag is found.
260 : *
261 : */
262 55 : static CHIP_ERROR FindHandler(const TLVReader & aReader, size_t aDepth, void * aContext)
263 : {
264 55 : VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
265 55 : const FindContext * const theContext = static_cast<const FindContext *>(aContext);
266 :
267 55 : if (theContext->mTag == aReader.GetTag())
268 : {
269 8 : theContext->mReader.Init(aReader);
270 : // terminate the iteration when the specified tag is found
271 8 : return CHIP_ERROR_SENTINEL;
272 : }
273 :
274 47 : return CHIP_NO_ERROR;
275 : }
276 :
277 : /**
278 : * Search for the specified tag within the provided TLV reader.
279 : *
280 : * @param[in] aReader A read-only reference to the TLV reader in
281 : * which to find the specified tag.
282 : * @param[in] aTag A read-only reference to the TLV tag to find.
283 : * @param[out] aResult A reference to storage to a TLV reader which
284 : * will be positioned at the specified tag
285 : * on success.
286 : *
287 : * @retval #CHIP_NO_ERROR On success.
288 : *
289 : * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified tag @a aTag was not found.
290 : *
291 : */
292 6 : CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult)
293 : {
294 6 : constexpr bool recurse = true;
295 6 : return Find(aReader, aTag, aResult, recurse);
296 : }
297 :
298 : /**
299 : * Search for the specified tag within the provided TLV reader,
300 : * optionally descending into arrays or structures.
301 : *
302 : * @param[in] aReader A read-only reference to the TLV reader in
303 : * which to find the specified tag.
304 : * @param[in] aTag A read-only reference to the TLV tag to find.
305 : * @param[out] aResult A reference to storage to a TLV reader which
306 : * will be positioned at the specified tag
307 : * on success.
308 : * @param[in] aRecurse A Boolean indicating whether (true) or not (false)
309 : * any encountered arrays or structures should be
310 : * descended into.
311 : *
312 : * @retval #CHIP_NO_ERROR On success.
313 : *
314 : * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified tag @a aTag was not found.
315 : *
316 : */
317 12 : CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult, const bool aRecurse)
318 : {
319 12 : FindContext theContext = { aTag, aResult };
320 12 : CHIP_ERROR retval = Iterate(aReader, FindHandler, &theContext, aRecurse);
321 :
322 12 : if (retval == CHIP_ERROR_SENTINEL)
323 8 : retval = CHIP_NO_ERROR;
324 : else
325 4 : retval = CHIP_ERROR_TLV_TAG_NOT_FOUND;
326 :
327 12 : return retval;
328 : }
329 :
330 : struct FindPredicateContext
331 : {
332 : TLVReader & mResult;
333 : IterateHandler mHandler;
334 : void * mContext;
335 : FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext);
336 : };
337 :
338 3 : FindPredicateContext::FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext) :
339 3 : mResult(inReader), mHandler(inHandler), mContext(inContext)
340 3 : {}
341 :
342 4 : static CHIP_ERROR FindPredicateHandler(const TLVReader & aReader, size_t aDepth, void * aContext)
343 : {
344 4 : FindPredicateContext * theContext = static_cast<FindPredicateContext *>(aContext);
345 : CHIP_ERROR err;
346 :
347 4 : err = theContext->mHandler(aReader, aDepth, theContext->mContext);
348 :
349 4 : if (err == CHIP_ERROR_SENTINEL)
350 2 : theContext->mResult.Init(aReader);
351 :
352 4 : return err;
353 : }
354 :
355 : /**
356 : * Search for the first element matching the predicate within the TLV reader
357 : * descending into arrays or structures. The @a aPredicate is applied
358 : * to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL
359 : * for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any
360 : * other value to terminate the search.
361 : *
362 : * @param[in] aReader A read-only reference to the TLV reader in which to find the
363 : * element matching the predicate.
364 : * @param[in] aPredicate A predicate to be applied to each TLV element. To
365 : * support the code reuse, aPredicate has the
366 : * IterateHandler type. The return value of aPredicate
367 : * controls the search: a #CHIP_ERROR_SENTINEL signals that
368 : * desired element has been found, #CHIP_NO_ERROR
369 : * signals that the desired element has not been found,
370 : * and all other values signal that the search should be
371 : * terminated.
372 : * @param[in] aContext An optional pointer to caller-provided context data.
373 : *
374 : * @param[out] aResult A reference to storage to a TLV reader which
375 : * will be positioned at the specified tag
376 : * on success.
377 : * @retval #CHIP_NO_ERROR On success.
378 : *
379 : * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified @a aPredicate did not locate the specified element
380 : *
381 : */
382 0 : CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult)
383 : {
384 0 : const bool recurse = true;
385 0 : return Find(aReader, aPredicate, aContext, aResult, recurse);
386 : }
387 :
388 : /**
389 : * Search for the first element matching the predicate within the TLV reader
390 : * optionally descending into arrays or structures. The @a aPredicate is applied
391 : * to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL
392 : * for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any
393 : * other value to terminate the search.
394 : *
395 : * @param[in] aReader A read-only reference to the TLV reader in which to find the
396 : * element matching the predicate.
397 : * @param[in] aPredicate A predicate to be applied to each TLV element. To
398 : * support the code reuse, aPredicate has the
399 : * @a IterateHandler type. The return value of aPredicate
400 : * controls the search: a #CHIP_ERROR_SENTINEL signals that
401 : * desired element has been found, #CHIP_NO_ERROR
402 : * signals that the desired element has not been found,
403 : * and all other values signal that the saerch should be
404 : * terminated.
405 : * @param[in] aContext An optional pointer to caller-provided context data.
406 : * @param[out] aResult A reference to storage to a TLV reader which
407 : * will be positioned at the specified tag
408 : * on success.
409 : * @param[in] aRecurse A boolean indicating whether (true) or not (false) any
410 : * encountered arrays or structures should be descended
411 : * into.
412 : *
413 : * @retval #CHIP_NO_ERROR On success.
414 : *
415 : * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified @a aPredicate did not locate the specified element
416 : *
417 : */
418 3 : CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult, const bool aRecurse)
419 : {
420 : CHIP_ERROR retval;
421 3 : FindPredicateContext theContext(aResult, aPredicate, aContext);
422 :
423 3 : retval = Iterate(aReader, FindPredicateHandler, &theContext, aRecurse);
424 :
425 3 : if (retval == CHIP_ERROR_SENTINEL)
426 2 : retval = CHIP_NO_ERROR;
427 : else
428 1 : retval = CHIP_ERROR_TLV_TAG_NOT_FOUND;
429 :
430 3 : return retval;
431 : }
432 :
433 : } // namespace Utilities
434 :
435 : } // namespace TLV
436 :
437 : } // namespace chip
|