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