Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2023 Project CHIP Authors
4 : * Copyright (c) 2013-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 contains definitions for working with data encoded in CHIP TLV format.
22 : *
23 : * CHIP TLV (Tag-Length-Value) is a generalized encoding method for simple structured data. It
24 : * shares many properties with the commonly used JSON serialization format while being considerably
25 : * more compact over the wire.
26 : */
27 : #pragma once
28 :
29 : #include <stddef.h>
30 : #include <stdint.h>
31 : #include <type_traits>
32 : #include <utility>
33 :
34 : #include <lib/core/CHIPError.h>
35 : #include <lib/core/DataModelTypes.h>
36 : #include <lib/core/Optional.h>
37 : #include <lib/core/TLVBackingStore.h>
38 : #include <lib/core/TLVTags.h>
39 : #include <lib/core/TLVTypes.h>
40 : #include <lib/support/BitFlags.h>
41 : #include <lib/support/BitMask.h>
42 : #include <lib/support/CodeUtils.h>
43 : #include <lib/support/DLLUtil.h>
44 : #include <lib/support/ScopedBuffer.h>
45 : #include <lib/support/Span.h>
46 :
47 : /**
48 : * @namespace chip::TLV
49 : *
50 : * Definitions for working with data encoded in CHIP TLV format.
51 : *
52 : * CHIP TLV is a generalized encoding method for simple structured data. It shares many properties
53 : * with the commonly used JSON serialization format while being considerably more compact over the wire.
54 : */
55 :
56 : namespace chip {
57 : namespace TLV {
58 :
59 : /**
60 : * Provides a memory efficient parser for data encoded in CHIP TLV format.
61 : *
62 : * TLVReader implements a forward-only, “pull-style” parser for CHIP TLV data. The TLVReader
63 : * object operates as a cursor that can be used to iterate over a sequence of TLV elements
64 : * and interpret their contents. When positioned on an element, applications can make calls
65 : * to the reader's Get() methods to query the current element’s type and tag, and to extract
66 : * any associated value. The reader’s Next() method is used to advance from element to element.
67 : *
68 : * A TLVReader object is always positioned either before, on or after a TLV element. When first
69 : * initialized, a TLVReader is positioned immediately before the first element of the encoding.
70 : * To begin reading, an application must make an initial call to the Next() method to position
71 : * the reader on the first element. When a container element is encountered--either a structure,
72 : * an array or a path--the OpenContainer() or EnterContainer() methods can be used to iterate
73 : * through the contents of the container.
74 : *
75 : * When the reader reaches the end of a TLV encoding, or the last element within a container,
76 : * it signals the application by returning a CHIP_END_OF_TLV error from the Next() method.
77 : * The reader will continue to return CHIP_END_OF_TLV until it is reinitialized, or the current
78 : * container is exited (via CloseContainer() / ExitContainer()).
79 : *
80 : * A TLVReader object can parse data directly from a fixed input buffer, or from memory provided
81 : * by a TLVBackingStore.
82 : */
83 : class DLL_EXPORT TLVReader
84 : {
85 : friend class TLVWriter;
86 : friend class TLVUpdater;
87 :
88 : public:
89 : TLVReader();
90 :
91 : /**
92 : * Initializes a TLVReader object from another TLVReader object.
93 : *
94 : * @param[in] aReader A read-only reference to the TLVReader to initialize
95 : * this from.
96 : *
97 : */
98 : void Init(const TLVReader & aReader);
99 :
100 : /**
101 : * Initializes a TLVReader object to read from a single input buffer.
102 : *
103 : * @param[in] data A pointer to a buffer containing the TLV data to be parsed.
104 : * @param[in] dataLen The length of the TLV data to be parsed.
105 : *
106 : */
107 : void Init(const uint8_t * data, size_t dataLen);
108 :
109 : /**
110 : * Initializes a TLVReader object to read from a single input buffer
111 : * represented as a span.
112 : *
113 : * @param[in] data A byte span to read from
114 : *
115 : */
116 2241 : void Init(const ByteSpan & data) { Init(data.data(), data.size()); }
117 :
118 : /**
119 : * Initializes a TLVReader object to read from a single input buffer
120 : * represented as byte array.
121 : *
122 : * @param[in] data A byte buffer to read from
123 : *
124 : */
125 : template <size_t N>
126 2 : void Init(const uint8_t (&data)[N])
127 : {
128 2 : Init(data, N);
129 2 : }
130 :
131 : /**
132 : * Initializes a TLVReader object to read from a TLVBackingStore.
133 : *
134 : * Parsing begins at the backing store's start position and continues until the
135 : * end of the data in the buffer, or maxLen bytes have been parsed.
136 : *
137 : * @param[in] backingStore A reference to a TLVBackingStore providing the TLV data to be parsed.
138 : * @param[in] maxLen The maximum number of bytes to parse. Defaults to the amount of data
139 : * in the input buffer.
140 : *
141 : * @retval #CHIP_NO_ERROR If the method succeeded.
142 : * @retval other Other error codes returned by TLVBackingStore::OnInit().
143 : */
144 : CHIP_ERROR Init(TLVBackingStore & backingStore, uint32_t maxLen = UINT32_MAX);
145 :
146 : /**
147 : * Advances the TLVReader object to the next TLV element to be read.
148 : *
149 : * The Next() method positions the reader object on the next element in a TLV encoding that resides
150 : * in the same containment context. In particular, if the reader is positioned at the outer-most
151 : * level of a TLV encoding, calling Next() will advance the reader to the next, top-most element.
152 : * If the reader is positioned within a TLV container element (a structure, array or path), calling
153 : * Next() will advance the reader to the next member element of the container.
154 : *
155 : * Since Next() constrains reader motion to the current containment context, calling Next() when
156 : * the reader is positioned on a container element will advance @em over the container, skipping
157 : * its member elements (and the members of any nested containers) until it reaches the first element
158 : * after the container.
159 : *
160 : * When there are no further elements within a particular containment context the Next() method will
161 : * return a #CHIP_END_OF_TLV error and the position of the reader will remain unchanged.
162 : *
163 : * @retval #CHIP_NO_ERROR If the reader was successfully positioned on a new element.
164 : * @retval #CHIP_END_OF_TLV If no further elements are available.
165 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
166 : * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
167 : * If the reader encountered an invalid or unsupported TLV element
168 : * type.
169 : * @retval #CHIP_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context.
170 : * @retval #CHIP_ERROR_UNKNOWN_IMPLICIT_TLV_TAG
171 : * If the reader encountered a implicitly-encoded TLV tag for which
172 : * the corresponding profile id is unknown.
173 : * @retval other Other CHIP or platform error codes returned by the configured
174 : * TLVBackingStore.
175 : *
176 : */
177 : CHIP_ERROR Next();
178 :
179 : /**
180 : * Advances the TLVReader object to the next TLV element to be read, asserting the tag of
181 : * the new element.
182 : *
183 : * This is a convenience method that combines the behavior of Next() and Expect(...).
184 : *
185 : * Note that if this method returns an error, the reader may or may not have been advanced already.
186 : * In use cases where this is important, separate calls to Next() and Expect(...) should be made.
187 : *
188 : * @retval #CHIP_NO_ERROR If the reader was successfully positioned on a new element
189 : * matching the expected parameters.
190 : * @retval other See return values of Next() and Expect().
191 : */
192 : CHIP_ERROR Next(Tag expectedTag);
193 :
194 : /**
195 : * Checks that the TLV reader is positioned at an element with the expected tag.
196 : *
197 : * @retval #CHIP_NO_ERROR If the reader is positioned on the expected element.
198 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the reader is not positioned on an element.
199 : * @retval #CHIP_ERROR_UNEXPECTED_TLV_ELEMENT
200 : * If the tag associated with the new element does not match the
201 : * value of the @p expectedTag argument.
202 : */
203 : CHIP_ERROR Expect(Tag expectedTag);
204 :
205 : /**
206 : * Advances the TLVReader object to the next TLV element to be read, asserting the type and tag of
207 : * the new element.
208 : *
209 : * This is a convenience method that combines the behavior of Next() and Expect(...).
210 : *
211 : * Note that if this method returns an error, the reader may or may not have been advanced already.
212 : * In use cases where this is important, separate calls to Next() and Expect(...) should be made.
213 : *
214 : * @retval #CHIP_NO_ERROR If the reader was successfully positioned on a new element
215 : * matching the expected parameters.
216 : * @retval other See return values of Next() and Expect().
217 : */
218 : CHIP_ERROR Next(TLVType expectedType, Tag expectedTag);
219 :
220 : /**
221 : * Checks that the TLV reader is positioned at an element with the expected type and tag.
222 : *
223 : * @param[in] expectedType The expected data type for the next element.
224 : * @param[in] expectedTag The expected tag for the next element.
225 : *
226 : * @retval #CHIP_NO_ERROR If the reader is positioned on the expected element.
227 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the type of the new element does not match the value
228 : * of the @p expectedType argument.
229 : * @retval #CHIP_ERROR_UNEXPECTED_TLV_ELEMENT
230 : * If the tag associated with the new element does not match the
231 : * value of the @p expectedTag argument.
232 : */
233 : CHIP_ERROR Expect(TLVType expectedType, Tag expectedTag);
234 :
235 : /**
236 : * Returns the type of the current TLV element.
237 : *
238 : * @return A TLVType value describing the data type of the current TLV element. If the reader
239 : * is not positioned on a TLV element, the return value will be kTLVType_NotSpecified.
240 : */
241 : TLVType GetType() const;
242 :
243 : /**
244 : * Returns the tag associated with current TLV element.
245 : *
246 : * The value returned by GetTag() can be used with the tag utility functions (IsProfileTag(),
247 : * IsContextTag(), ProfileIdFromTag(), etc.) to determine the type of tag and to extract various tag
248 : * field values.
249 : *
250 : * @note If the reader is not positioned on a TLV element when GetTag() is called, the return value
251 : * is undefined. Therefore whenever the position of the reader is uncertain applications should call
252 : * GetType() to determine if the reader is position on an element (GetType() != kTLVType_NotSpecified)
253 : * before calling GetTag().
254 : *
255 : * @return An unsigned integer containing information about the tag associated with the current
256 : * TLV element.
257 : */
258 1246297 : Tag GetTag() const { return mElemTag; }
259 :
260 : /**
261 : * Returns the length of data associated with current TLV element.
262 : *
263 : * Data length only applies to elements of type UTF8 string or byte string. For UTF8 strings, the
264 : * value returned is the number of bytes in the string, not the number of characters.
265 : *
266 : * @return The length (in bytes) of data associated with the current TLV element, or 0 if the
267 : * current element is not a UTF8 string or byte string, or if the reader is not
268 : * positioned on an element.
269 : */
270 : uint32_t GetLength() const;
271 :
272 : /**
273 : * Returns the control byte associated with current TLV element.
274 : *
275 : * Ideally, nobody ever needs to know about the control byte and only the
276 : * internal implementation of TLV should have access to it. But, nevertheless,
277 : * having access to the control byte is helpful for debugging purposes by the
278 : * TLV Debug Utilities (that try to decode the tag control byte when pretty
279 : * printing the TLV buffer contents).
280 : *
281 : * @note Unless you really know what you are doing, please refrain from using
282 : * this method and the associated control byte information.
283 : *
284 : * @return An unsigned integer containing the control byte associated with
285 : * the current TLV element. kTLVControlByte_NotSpecified is
286 : * returned if the reader is not positioned @em on an element.
287 : */
288 1390 : uint16_t GetControlByte() const { return mControlByte; }
289 :
290 : /**
291 : * Get the value of the current element as a bool type.
292 : *
293 : * @param[out] v Receives the value associated with current TLV element.
294 : *
295 : * @retval #CHIP_NO_ERROR If the method succeeded.
296 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV boolean type, or the
297 : * reader is not positioned on an element.
298 : */
299 : CHIP_ERROR Get(bool & v) const;
300 :
301 : /**
302 : * Get the value of the current element as an 8-bit signed integer.
303 : *
304 : * If the encoded integer value is larger than the output data type the resultant value will be
305 : * truncated.
306 : *
307 : * @param[out] v Receives the value associated with current TLV element.
308 : *
309 : * @retval #CHIP_NO_ERROR If the method succeeded.
310 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
311 : * unsigned), or the reader is not positioned on an element.
312 : *
313 : */
314 : CHIP_ERROR Get(int8_t & v) const;
315 :
316 : /**
317 : * Get the value of the current element as a 16-bit signed integer.
318 : *
319 : * If the encoded integer value is larger than the output data type the resultant value will be
320 : * truncated.
321 : *
322 : * @param[out] v Receives the value associated with current TLV element.
323 : *
324 : * @retval #CHIP_NO_ERROR If the method succeeded.
325 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
326 : * unsigned), or the reader is not positioned on an element.
327 : *
328 : */
329 : CHIP_ERROR Get(int16_t & v) const;
330 :
331 : /**
332 : * Get the value of the current element as a 32-bit signed integer.
333 : *
334 : * If the encoded integer value is larger than the output data type the resultant value will be
335 : * truncated.
336 : *
337 : * @param[out] v Receives the value associated with current TLV element.
338 : *
339 : * @retval #CHIP_NO_ERROR If the method succeeded.
340 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
341 : * unsigned), or the reader is not positioned on an element.
342 : *
343 : */
344 : CHIP_ERROR Get(int32_t & v) const;
345 :
346 : /**
347 : * Get the value of the current element as a 64-bit signed integer.
348 : *
349 : * If the encoded integer value is larger than the output data type the resultant value will be
350 : * truncated.
351 : *
352 : * @param[out] v Receives the value associated with current TLV element.
353 : *
354 : * @retval #CHIP_NO_ERROR If the method succeeded.
355 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
356 : * unsigned), or the reader is not positioned on an element.
357 : *
358 : */
359 : CHIP_ERROR Get(int64_t & v) const;
360 :
361 : /**
362 : * Get the value of the current element as an 8-bit unsigned integer.
363 : *
364 : * If the encoded integer value is larger than the output data type the resultant value will be
365 : * truncated. Similarly, if the encoded integer value is negative, the value will be converted
366 : * to unsigned.
367 : *
368 : * @param[out] v Receives the value associated with current TLV element.
369 : *
370 : * @retval #CHIP_NO_ERROR If the method succeeded.
371 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
372 : * unsigned), or the reader is not positioned on an element.
373 : *
374 : */
375 : CHIP_ERROR Get(uint8_t & v) const;
376 :
377 : /**
378 : * Get the value of the current element as a 16-bit unsigned integer.
379 : *
380 : * If the encoded integer value is larger than the output data type the resultant value will be
381 : * truncated. Similarly, if the encoded integer value is negative, the value will be converted
382 : * to unsigned.
383 : *
384 : * @param[out] v Receives the value associated with current TLV element.
385 : *
386 : * @retval #CHIP_NO_ERROR If the method succeeded.
387 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
388 : * unsigned), or the reader is not positioned on an element.
389 : *
390 : */
391 : CHIP_ERROR Get(uint16_t & v) const;
392 :
393 : /**
394 : * Get the value of the current element as a 32-bit unsigned integer.
395 : *
396 : * If the encoded integer value is larger than the output data type the resultant value will be
397 : * truncated. Similarly, if the encoded integer value is negative, the value will be converted
398 : * to unsigned.
399 : *
400 : * @param[out] v Receives the value associated with current TLV element.
401 : *
402 : * @retval #CHIP_NO_ERROR If the method succeeded.
403 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
404 : unsigned), or the reader is not positioned on an element.
405 : *
406 : */
407 : CHIP_ERROR Get(uint32_t & v) const;
408 :
409 : /**
410 : * Get the value of the current element as a 64-bit unsigned integer.
411 : *
412 : * If the encoded integer value is negative, the value will be converted to unsigned.
413 : *
414 : * @param[out] v Receives the value associated with current TLV element.
415 : *
416 : * @retval #CHIP_NO_ERROR If the method succeeded.
417 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or
418 : * unsigned), or the reader is not positioned on an element.
419 : *
420 : */
421 : CHIP_ERROR Get(uint64_t & v) const;
422 :
423 : /**
424 : * Get the value of the current element as a double-precision floating point number.
425 : *
426 : * @param[out] v Receives the value associated with current TLV element.
427 : *
428 : * @retval #CHIP_NO_ERROR If the method succeeded.
429 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV floating point type, or
430 : * the reader is not positioned on an element.
431 : *
432 : */
433 : CHIP_ERROR Get(double & v) const;
434 :
435 : /**
436 : * Get the value of the current element as a single-precision floating point number.
437 : *
438 : * @param[out] v Receives the value associated with current TLV element.
439 : *
440 : * @retval #CHIP_NO_ERROR If the method succeeded.
441 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV floating point type, or
442 : * the reader is not positioned on an element.
443 : *
444 : */
445 : CHIP_ERROR Get(float & v) const;
446 :
447 : /**
448 : * Get the value of the current element as a ByteSpan
449 : *
450 : * @param[out] v Receives the value associated with current TLV element.
451 : *
452 : * @retval #CHIP_NO_ERROR If the method succeeded.
453 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV bytes array, or
454 : * the reader is not positioned on an element.
455 : *
456 : */
457 : CHIP_ERROR Get(ByteSpan & v) const;
458 :
459 : /**
460 : * Get the value of the current element as a FixedByteSpan
461 : *
462 : * @param[out] v Receives the value associated with current TLV element.
463 : *
464 : * @retval #CHIP_NO_ERROR If the method succeeded.
465 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV bytes array, or
466 : * the reader is not positioned on an element.
467 : *
468 : */
469 : template <size_t N>
470 11094 : CHIP_ERROR Get(FixedByteSpan<N> & v) const
471 : {
472 : const uint8_t * val;
473 11094 : ReturnErrorOnFailure(GetDataPtr(val));
474 11094 : VerifyOrReturnError(GetLength() == N, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
475 11076 : v = FixedByteSpan<N>(val);
476 11076 : return CHIP_NO_ERROR;
477 : }
478 :
479 : /**
480 : * Get the value of the current element as a CharSpan
481 : *
482 : * @param[out] v Receives the value associated with current TLV element.
483 : *
484 : * @retval #CHIP_NO_ERROR If the method succeeded.
485 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV character string, or
486 : * the reader is not positioned on an element.
487 : *
488 : */
489 : CHIP_ERROR Get(CharSpan & v) const;
490 :
491 : /**
492 : * Get the Localized String Identifier contained in the current element..
493 : *
494 : * The method takes what's after the first Information Separator 1 <IS1>, and until end of string
495 : * or second <IS1>, and return the hex-decoded string identifier, if one was there.
496 : *
497 : * @param[out] lsid Optional Localized String Identifier. Returns empty
498 : * if the value is not found or it was invalidly encoded.
499 : *
500 : * @retval #CHIP_NO_ERROR If the method succeeded.
501 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV character string, or
502 : * the reader is not positioned on an element.
503 : * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT If the Localized String Identifier is malformed.
504 : */
505 : CHIP_ERROR Get(Optional<LocalizedStringIdentifier> & lsid);
506 :
507 : /**
508 : * Get the value of the current element as an enum value, if it's an integer
509 : * value that fits in the enum type.
510 : *
511 : * @param[out] v Receives the value associated with current TLV element.
512 : */
513 : template <typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
514 3100 : CHIP_ERROR Get(T & v)
515 : {
516 : std::underlying_type_t<T> val;
517 3100 : ReturnErrorOnFailure(Get(val));
518 3100 : v = static_cast<T>(val);
519 3100 : return CHIP_NO_ERROR;
520 : }
521 :
522 : /**
523 : * Get the value of the current element as a BitFlags value, if it's an integer
524 : * value that fits in the BitFlags type.
525 : *
526 : * @param[out] v Receives the value associated with current TLV element.
527 : */
528 : template <typename T>
529 0 : CHIP_ERROR Get(BitFlags<T> & v)
530 : {
531 : std::underlying_type_t<T> val;
532 0 : ReturnErrorOnFailure(Get(val));
533 0 : v.SetRaw(val);
534 0 : return CHIP_NO_ERROR;
535 : }
536 :
537 : /**
538 : * Get the value of the current element as a BitMask value, if it's an integer
539 : * value that fits in the BitMask type.
540 : *
541 : * @param[out] v Receives the value associated with current TLV element.
542 : */
543 : template <typename T>
544 : CHIP_ERROR Get(BitMask<T> & v)
545 : {
546 : std::underlying_type_t<T> val;
547 : ReturnErrorOnFailure(Get(val));
548 : v.SetRaw(val);
549 : return CHIP_NO_ERROR;
550 : }
551 :
552 : /**
553 : * Get the value of the current byte or UTF8 string element.
554 : *
555 : * To determine the required input buffer size, call the GetLength() method before calling GetBytes().
556 : *
557 : * @note The data output by this method is NOT null-terminated.
558 : *
559 : * @param[in] buf A pointer to a buffer to receive the string data.
560 : * @param[in] bufSize The size in bytes of the buffer pointed to by @p buf.
561 : *
562 : * @retval #CHIP_NO_ERROR If the method succeeded.
563 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or
564 : * the reader is not positioned on an element.
565 : * @retval #CHIP_ERROR_BUFFER_TOO_SMALL
566 : * If the supplied buffer is too small to hold the data associated
567 : * with the current element.
568 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
569 : * @retval other Other CHIP or platform error codes returned by the configured
570 : * TLVBackingStore.
571 : *
572 : */
573 : CHIP_ERROR GetBytes(uint8_t * buf, size_t bufSize);
574 :
575 : /**
576 : * Allocates and returns a buffer containing the value of the current byte or UTF8 string.
577 : *
578 : * This method creates a buffer for and returns a copy of the data associated with the byte
579 : * or UTF-8 string element at the current position. Memory for the buffer is obtained with
580 : * Platform::MemoryAlloc() and should be freed with Platform::MemoryFree() by the caller when
581 : * it is no longer needed.
582 : *
583 : * @note The data returned by this method is NOT null-terminated.
584 : *
585 : * @param[out] buf A reference to a pointer to which a heap-allocated buffer of
586 : * @p dataLen bytes will be assigned on success.
587 : * @param[out] dataLen A reference to storage for the size, in bytes, of @p buf on
588 : * success.
589 : *
590 : * @retval #CHIP_NO_ERROR If the method succeeded.
591 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or
592 : * the reader is not positioned on an element.
593 : * @retval #CHIP_ERROR_NO_MEMORY If memory could not be allocated for the output buffer.
594 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
595 : * @retval other Other CHIP or platform error codes returned by the configured
596 : * TLVBackingStore.
597 : *
598 : */
599 : CHIP_ERROR DupBytes(uint8_t *& buf, uint32_t & dataLen);
600 :
601 : /**
602 : * Get the value of the current byte or UTF8 string element as a null terminated string.
603 : *
604 : * To determine the required input buffer size, call the GetLength() method before calling GetBytes().
605 : * The input buffer should be at least one byte bigger than the string length to accommodate the null
606 : * character.
607 : *
608 : * @param[in] buf A pointer to a buffer to receive the byte string data.
609 : * @param[in] bufSize The size in bytes of the buffer pointed to by @p buf.
610 : *
611 : * @retval #CHIP_NO_ERROR If the method succeeded.
612 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or
613 : * the reader is not positioned on an element.
614 : * @retval #CHIP_ERROR_BUFFER_TOO_SMALL
615 : * If the supplied buffer is too small to hold the data associated
616 : * with the current element.
617 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
618 : * @retval other Other CHIP or platform error codes returned by the configured
619 : * TLVBackingStore.
620 : *
621 : */
622 : CHIP_ERROR GetString(char * buf, size_t bufSize);
623 :
624 : /**
625 : * Allocates and returns a buffer containing the null-terminated value of the current byte or UTF8
626 : * string.
627 : *
628 : * This method creates a buffer for and returns a null-terminated copy of the data associated with
629 : * the byte or UTF-8 string element at the current position. Memory for the buffer is obtained with
630 : * Platform::MemoryAlloc() and should be freed with chip::Platform::MemoryFree() by the caller when
631 : * it is no longer needed.
632 : *
633 : * @param[out] buf A reference to a pointer to which a heap-allocated buffer of
634 : * will be assigned on success.
635 : *
636 : * @retval #CHIP_NO_ERROR If the method succeeded.
637 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or
638 : * the reader is not positioned on an element.
639 : * @retval #CHIP_ERROR_NO_MEMORY If memory could not be allocated for the output buffer.
640 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
641 : * @retval other Other CHIP or platform error codes returned by the configured
642 : * TLVBackingStore.
643 : *
644 : */
645 : CHIP_ERROR DupString(char *& buf);
646 :
647 : /**
648 : * Get a pointer to the initial encoded byte of a TLV byte or UTF8 string element.
649 : *
650 : * This method returns a direct pointer to the encoded string value within the underlying input buffer
651 : * as fetched by `Next`. To succeed, the method requires that the entirety of the
652 : * string value be present in a single buffer.
653 : *
654 : * If no string data is present (i.e the length is zero), data shall be updated to point to null.
655 : *
656 : * @param[out] data A reference to a const pointer that will receive a pointer to
657 : * the underlying string data.
658 : *
659 : * @retval #CHIP_NO_ERROR If the method succeeded.
660 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or the
661 : * reader is not positioned on an element.
662 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely or the value
663 : * of the current string element is not contained within a single
664 : * contiguous buffer.
665 : * @retval other Other CHIP or platform error codes returned by the configured
666 : * TLVBackingStore.
667 : *
668 : */
669 : CHIP_ERROR GetDataPtr(const uint8_t *& data) const;
670 :
671 : /**
672 : * Prepares a TLVReader object for reading the members of TLV container element.
673 : *
674 : * The EnterContainer() method prepares the current TLVReader object to begin reading the member
675 : * elements of a TLV container (a structure, array or path). For every call to EnterContainer()
676 : * applications must make a corresponding call to ExitContainer().
677 : *
678 : * When EnterContainer() is called the TLVReader object must be positioned on the container element
679 : * to be read. The method takes as an argument a reference to a TLVType value which will be used
680 : * to save the context of the reader while it is reading the container.
681 : *
682 : * When the EnterContainer() method returns, the reader is positioned immediately @em before the
683 : * first member of the container. Repeatedly calling Next() will advance the reader through the members
684 : * of the collection until the end is reached, at which point the reader will return CHIP_END_OF_TLV.
685 : *
686 : * Once the application has finished reading a container it can continue reading the elements after
687 : * the container by calling the ExitContainer() method.
688 : *
689 : * @param[out] outerContainerType A reference to a TLVType value that will receive the context
690 : * of the reader.
691 : *
692 : * @retval #CHIP_NO_ERROR If the method succeeded.
693 : * @retval #CHIP_ERROR_INCORRECT_STATE If the current element is not positioned on a container element.
694 : *
695 : */
696 : CHIP_ERROR EnterContainer(TLVType & outerContainerType);
697 :
698 : /**
699 : * Completes the reading of a TLV container and prepares a TLVReader object to read elements
700 : * after the container.
701 : *
702 : * The ExitContainer() method restores the state of a TLVReader object after a call to
703 : * EnterContainer(). For every call to EnterContainer() applications must make a corresponding
704 : * call to ExitContainer(), passing the context value returned by the EnterContainer() method.
705 : *
706 : * When ExitContainer() returns, the reader is positioned immediately before the first element that
707 : * follows the container. From this point an application can use the Next() method to advance
708 : * through any remaining elements.
709 : *
710 : * Once EnterContainer() has been called, applications can call ExitContainer() on a reader at any
711 : * point in time, regardless of whether all elements in the underlying container have been read.
712 : *
713 : * @note Any changes made to the configuration of the reader between the calls to EnterContainer()
714 : * and ExitContainer() are NOT undone by the call to ExitContainer(). For example, a change to the
715 : * implicit profile id (@p ImplicitProfileId) will not be reversed when a container is exited. Thus
716 : * it is the application's responsibility to adjust the configuration accordingly at the appropriate
717 : * times.
718 : *
719 : * @param[in] outerContainerType The TLVType value that was returned by the EnterContainer() method.
720 : *
721 : * @retval #CHIP_NO_ERROR If the method succeeded.
722 : * @retval #CHIP_ERROR_INCORRECT_STATE If OpenContainer() has not been called on the reader, or if
723 : * the container reader does not match the one passed to the
724 : * OpenContainer() method.
725 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
726 : * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
727 : * If the reader encountered an invalid or unsupported TLV element type.
728 : * @retval #CHIP_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context.
729 : * @retval other Other CHIP or platform error codes returned by the configured
730 : * TLVBackingStore.
731 : *
732 : */
733 : CHIP_ERROR ExitContainer(TLVType outerContainerType);
734 :
735 : /**
736 : * Initializes a new TLVReader object for reading the members of a TLV container element.
737 : *
738 : * The OpenContainer() method initializes a new TLVReader object for reading the member elements of a
739 : * TLV container (a structure, array or path). When OpenContainer() is called, the current TLVReader
740 : * object must be positioned on the container element to be read. The method takes as its sole argument
741 : * a reference to a new reader that will be initialized to read the container. This reader is known as
742 : * the <em>container reader</em> while the reader on which OpenContainer() is called is known as the <em>parent
743 : * reader</em>.
744 : *
745 : * When the OpenContainer() method returns, the container reader is positioned immediately before the
746 : * first member of the container. Calling Next() on the container reader will advance through the members
747 : * of the collection until the end is reached, at which point the reader will return CHIP_END_OF_TLV.
748 : *
749 : * While the container reader is open, applications must not make calls on or otherwise alter the state
750 : * of the parent reader. Once an application has finished using the container reader it must close it
751 : * by calling CloseContainer() on the parent reader, passing the container reader as an argument.
752 : * Applications may close the container reader at any point, with or without reading all elements
753 : * contained in the underlying container. After the container reader is closed, applications may
754 : * continue their use of the parent reader.
755 : *
756 : * The container reader inherits various configuration properties from the parent reader. These are:
757 : *
758 : * @li The implicit profile id (ImplicitProfileId)
759 : * @li The application data pointer (AppData)
760 : * @li The GetNextBuffer function pointer
761 : *
762 : * @note The EnterContainer() method can be used as an alternative to OpenContainer() to read a
763 : * container element without initializing a new reader object.
764 : *
765 : * @param[out] containerReader A reference to a TLVReader object that will be initialized for
766 : * reading the members of the current container element. Any data
767 : * associated with the supplied object is overwritten.
768 : *
769 : * @retval #CHIP_NO_ERROR If the method succeeded.
770 : * @retval #CHIP_ERROR_INCORRECT_STATE If the current element is not positioned on a container element.
771 : *
772 : */
773 : CHIP_ERROR OpenContainer(TLVReader & containerReader);
774 :
775 : /**
776 : * Completes the reading of a TLV container after a call to OpenContainer().
777 : *
778 : * The CloseContainer() method restores the state of a parent TLVReader object after a call to
779 : * OpenContainer(). For every call to OpenContainer() applications must make a corresponding
780 : * call to CloseContainer(), passing a reference to the same container reader to both methods.
781 : *
782 : * When CloseContainer() returns, the parent reader is positioned immediately before the first
783 : * element that follows the container. From this point an application can use the Next() method
784 : * to advance through any remaining elements.
785 : *
786 : * Applications can call close CloseContainer() on a parent reader at any point in time, regardless
787 : * of whether all elements in the underlying container have been read. After CloseContainer() has
788 : * been called, the application should consider the container reader 'de-initialized' and must not
789 : * use it further without re-initializing it.
790 : *
791 : * @param[in] containerReader A reference to the TLVReader object that was supplied to the
792 : * OpenContainer() method.
793 : *
794 : * @retval #CHIP_NO_ERROR If the method succeeded.
795 : * @retval #CHIP_ERROR_INCORRECT_STATE If OpenContainer() has not been called on the reader, or if
796 : * the container reader does not match the one passed to the
797 : * OpenContainer() method.
798 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
799 : * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
800 : * If the reader encountered an invalid or unsupported TLV
801 : * element type.
802 : * @retval #CHIP_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context.
803 : * @retval other Other CHIP or platform error codes returned by the configured
804 : * TLVBackingStore.
805 : *
806 : */
807 : CHIP_ERROR CloseContainer(TLVReader & containerReader);
808 :
809 : /**
810 : * Returns the type of the container within which the TLVReader is currently reading.
811 : *
812 : * The GetContainerType() method returns the type of the TLV container within which the TLVReader
813 : * is reading. If the TLVReader is positioned at the outer-most level of a TLV encoding (i.e. before,
814 : * on or after the outer-most TLV element), the method will return kTLVType_NotSpecified.
815 : *
816 : * @return The TLVType of the current container, or kTLVType_NotSpecified if the TLVReader is not
817 : * positioned within a container.
818 : */
819 5 : TLVType GetContainerType() const { return mContainerType; }
820 :
821 : /**
822 : * Verifies that the TLVReader object is at the end of a TLV container.
823 : *
824 : * The VerifyEndOfContainer() method verifies that there are no further TLV elements to be read
825 : * within the current TLV container. This is a convenience method that is equivalent to calling
826 : * Next() and checking for a return value of CHIP_END_OF_TLV.
827 : *
828 : * @note When there are more TLV elements in the collection, this method will change the position
829 : * of the reader.
830 : *
831 : * @retval #CHIP_NO_ERROR If there are no further TLV elements to be read.
832 : * @retval #CHIP_ERROR_UNEXPECTED_TLV_ELEMENT
833 : * If another TLV element was found in the collection.
834 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
835 : * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
836 : * If the reader encountered an invalid or unsupported TLV element
837 : * type.
838 : * @retval #CHIP_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context.
839 : * @retval other Other CHIP or platform error codes returned by the configured
840 : * TLVBackingStore.
841 : *
842 : */
843 : CHIP_ERROR VerifyEndOfContainer();
844 :
845 : /**
846 : * Returns the total number of bytes read since the reader was initialized.
847 : *
848 : * @return Total number of bytes read since the reader was initialized.
849 : */
850 53126 : uint32_t GetLengthRead() const { return mLenRead; }
851 :
852 : /**
853 : * Returns the total number of bytes that can be read until the max read length is reached.
854 : *
855 : * @return Total number of bytes that can be read until the max read length is reached.
856 : */
857 0 : uint32_t GetRemainingLength() const { return mMaxLen - mLenRead; }
858 :
859 : /**
860 : * Return the total number of bytes for the TLV data
861 : * @return the total number of bytes for the TLV data
862 : */
863 106 : uint32_t GetTotalLength() const { return mMaxLen; }
864 :
865 : /**
866 : * Returns the stored backing store.
867 : *
868 : * @return the stored backing store.
869 : */
870 4 : TLVBackingStore * GetBackingStore() { return mBackingStore; }
871 :
872 : /**
873 : * Returns true if the current TLV element type is a double.
874 : */
875 53 : bool IsElementDouble() { return ElementType() == TLVElementType::FloatingPointNumber64; }
876 :
877 : /**
878 : * Gets the point in the underlying input buffer that corresponds to the reader's current position.
879 : *
880 : * @note Depending on the type of the current element, GetReadPoint() will return a pointer that
881 : * is some number of bytes *after* the first byte of the element. For string types (UTF8 and byte
882 : * strings), the pointer will point to the first byte of the string's value. For container types
883 : * (structures, arrays and paths), the pointer will point to the first member element within the
884 : * container. For all other types, the pointer will point to the byte immediately after the element's
885 : * encoding.
886 : *
887 : * @return A pointer into underlying input buffer that corresponds to the reader's current position.
888 : */
889 1530 : const uint8_t * GetReadPoint() const { return mReadPoint; }
890 :
891 : /**
892 : * Advances the TLVReader object to immediately after the current TLV element.
893 : *
894 : * The Skip() method positions the reader object immediately @em after the current TLV element, such
895 : * that a subsequent call to Next() will advance the reader to the following element. Like Next(),
896 : * if the reader is positioned on a container element at the time of the call, the members of the
897 : * container will be skipped. If the reader is not positioned on any element, its position remains
898 : * unchanged.
899 : *
900 : * @retval #CHIP_NO_ERROR If the reader was successfully positioned on a new element.
901 : * @retval #CHIP_END_OF_TLV If no further elements are available.
902 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely.
903 : * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
904 : * If the reader encountered an invalid or unsupported TLV
905 : * element type.
906 : * @retval #CHIP_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context.
907 : * @retval other Other CHIP or platform error codes returned by the configured
908 : * TLVBackingStore.
909 : *
910 : */
911 : CHIP_ERROR Skip();
912 :
913 : /**
914 : * Position the destination reader on the next element with the given tag within this reader's current container context
915 : *
916 : * @param[in] tagInApiForm The destination context tag value
917 : * @param[in] destReader The destination TLV reader value that was located by given tag
918 : *
919 : * @retval #CHIP_NO_ERROR If the reader was successfully positioned at the given tag
920 : * @retval #CHIP_END_OF_TLV If the given tag cannot be found
921 : * @retval other Other CHIP or platform error codes
922 : */
923 : CHIP_ERROR FindElementWithTag(Tag tagInApiForm, TLVReader & destReader) const;
924 :
925 : /**
926 : * Count how many elements remain in the currently-open container. Will
927 : * fail with CHIP_ERROR_INCORRECT_STATE if not currently in a container.
928 : *
929 : * @param[out] size On success, set to the number of items following the
930 : * current reader position in the container.
931 : */
932 : CHIP_ERROR CountRemainingInContainer(size_t * size) const;
933 :
934 : /**
935 : * The profile id to be used for profile tags encoded in implicit form.
936 : *
937 : * When the reader encounters a profile-specific tag that has been encoded in implicit form, it
938 : * uses the value of the @p ImplicitProfileId property as the assumed profile id for the tag.
939 : *
940 : * By default, the @p ImplicitProfileId property is set to kProfileIdNotSpecified. When decoding
941 : * TLV that contains implicitly-encoded tags, applications must set @p ImplicitProfileId prior
942 : * to reading any TLV elements having such tags. The appropriate profile id is usually dependent
943 : * on the context of the application or protocol being spoken.
944 : *
945 : * If an implicitly-encoded tag is encountered while @p ImplicitProfileId is set to
946 : * kProfileIdNotSpecified, the reader will return a #CHIP_ERROR_UNKNOWN_IMPLICIT_TLV_TAG error.
947 : */
948 : uint32_t ImplicitProfileId;
949 :
950 : /**
951 : * A pointer field that can be used for application-specific data.
952 : */
953 : void * AppData;
954 :
955 : protected:
956 : Tag mElemTag;
957 : uint64_t mElemLenOrVal;
958 : TLVBackingStore * mBackingStore;
959 : const uint8_t * mReadPoint;
960 : const uint8_t * mBufEnd;
961 : uint32_t mLenRead;
962 : uint32_t mMaxLen;
963 : TLVType mContainerType;
964 : uint16_t mControlByte;
965 :
966 : private:
967 : bool mContainerOpen;
968 :
969 : protected:
970 563390 : bool IsContainerOpen() const { return mContainerOpen; }
971 4219899 : void SetContainerOpen(bool aContainerOpen) { mContainerOpen = aContainerOpen; }
972 :
973 : CHIP_ERROR ReadElement();
974 : void ClearElementState();
975 : CHIP_ERROR SkipData();
976 : CHIP_ERROR SkipToEndOfContainer();
977 : CHIP_ERROR VerifyElement();
978 : Tag ReadTag(TLVTagControl tagControl, const uint8_t *& p) const;
979 : CHIP_ERROR EnsureData(CHIP_ERROR noDataErr);
980 : CHIP_ERROR ReadData(uint8_t * buf, uint32_t len);
981 : CHIP_ERROR GetElementHeadLength(uint8_t & elemHeadBytes) const;
982 : TLVElementType ElementType() const;
983 : };
984 :
985 : /*
986 : * A TLVReader that is backed by a scoped memory buffer that is owned by the reader
987 : */
988 : class ScopedBufferTLVReader : public TLVReader
989 : {
990 : public:
991 : /*
992 : * Construct and initialize the reader by taking ownership of the provided scoped buffer.
993 : */
994 475 : ScopedBufferTLVReader(Platform::ScopedMemoryBuffer<uint8_t> && buffer, size_t dataLen) { Init(std::move(buffer), dataLen); }
995 :
996 1023 : ScopedBufferTLVReader() {}
997 :
998 : /*
999 : * Initialize the reader by taking ownership of a passed in scoped buffer.
1000 : */
1001 1517 : void Init(Platform::ScopedMemoryBuffer<uint8_t> && buffer, size_t dataLen)
1002 : {
1003 1517 : mBuffer = std::move(buffer);
1004 1517 : TLVReader::Init(mBuffer.Get(), dataLen);
1005 1517 : }
1006 :
1007 : /*
1008 : * Take back the buffer owned by the reader and transfer its ownership to
1009 : * the provided buffer reference. This also re-initializes the reader with
1010 : * a null buffer to prevent further use of the reader.
1011 : */
1012 : void TakeBuffer(Platform::ScopedMemoryBuffer<uint8_t> & buffer)
1013 : {
1014 : buffer = std::move(mBuffer);
1015 : TLVReader::Init(nullptr, 0);
1016 : }
1017 :
1018 : private:
1019 : Platform::ScopedMemoryBuffer<uint8_t> mBuffer;
1020 : };
1021 :
1022 : /**
1023 : * A TLVReader that is guaranteed to be backed by a single contiguous buffer.
1024 : * This allows it to expose some additional methods that allow consumers to
1025 : * directly access the data in that buffer in a safe way that is guaranteed to
1026 : * work as long as the reader object stays in scope.
1027 : */
1028 : class ContiguousBufferTLVReader : public TLVReader
1029 : {
1030 : public:
1031 14265 : ContiguousBufferTLVReader() : TLVReader() {}
1032 :
1033 : /**
1034 : * Init with input buffer as ptr + length pair.
1035 : */
1036 14255 : void Init(const uint8_t * data, size_t dataLen) { TLVReader::Init(data, dataLen); }
1037 :
1038 : /**
1039 : * Init with input buffer as ByteSpan.
1040 : */
1041 263 : void Init(const ByteSpan & data) { Init(data.data(), data.size()); }
1042 :
1043 : /**
1044 : * Init with input buffer as byte array.
1045 : */
1046 : template <size_t N>
1047 : void Init(const uint8_t (&data)[N])
1048 : {
1049 : Init(data, N);
1050 : }
1051 :
1052 : /**
1053 : * Allow opening a container, with a new ContiguousBufferTLVReader reading
1054 : * that container. See TLVReader::OpenContainer for details.
1055 : */
1056 : CHIP_ERROR OpenContainer(ContiguousBufferTLVReader & containerReader);
1057 :
1058 : /**
1059 : * Get the value of the current UTF8 string as a Span<const char> pointing
1060 : * into the TLV data. Consumers may need to copy the data elsewhere as
1061 : * needed (e.g. before releasing the reader and its backing buffer if they
1062 : * plan to use the data after that point).
1063 : *
1064 : * @param[out] data A Span<const char> representing the string data.
1065 : *
1066 : * @retval #CHIP_NO_ERROR If the method succeeded.
1067 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV UTF8 string, or
1068 : * the reader is not positioned on an element.
1069 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely (i.e. the string length was "too big").
1070 : *
1071 : */
1072 : CHIP_ERROR GetStringView(Span<const char> & data);
1073 :
1074 : /**
1075 : * Get the value of the current octet string as a ByteSpan pointing into the
1076 : * TLV data. Consumers may need to copy the data elsewhere as needed
1077 : * (e.g. before releasing the reader and its backing buffer if they plan to
1078 : * use the data after that point).
1079 : *
1080 : * @param[out] data A ByteSpan representing the string data.
1081 : *
1082 : * @retval #CHIP_NO_ERROR If the method succeeded.
1083 : * @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV octet string, or
1084 : * the reader is not positioned on an element.
1085 : * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely (i.e. the string length was "too big").
1086 : *
1087 : */
1088 : CHIP_ERROR GetByteView(ByteSpan & data);
1089 : };
1090 :
1091 : } // namespace TLV
1092 : } // namespace chip
|