Matter SDK Coverage Report
Current view: top level - lib/core - TLVUpdater.h (source / functions) Coverage Total Hit
Test: SHA:4d2388ac7eed75b2fe5e05e20de377999c632502 Lines: 100.0 % 12 12
Test Date: 2025-07-27 07:17:09 Functions: 100.0 % 11 11

            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              : #pragma once
      19              : 
      20              : #include <stdint.h>
      21              : 
      22              : #include <lib/core/CHIPError.h>
      23              : #include <lib/core/TLVReader.h>
      24              : #include <lib/core/TLVTags.h>
      25              : #include <lib/core/TLVTypes.h>
      26              : #include <lib/core/TLVWriter.h>
      27              : #include <lib/support/DLLUtil.h>
      28              : #include <lib/support/Span.h>
      29              : 
      30              : /**
      31              :  * @namespace chip::TLV
      32              :  *
      33              :  * Definitions for working with data encoded in CHIP TLV format.
      34              :  *
      35              :  * CHIP TLV is a generalized encoding method for simple structured data. It shares many properties
      36              :  * with the commonly used JSON serialization format while being considerably more compact over the wire.
      37              :  */
      38              : 
      39              : namespace chip {
      40              : namespace TLV {
      41              : 
      42              : /**
      43              :  * Provides a unified Reader/Writer interface for editing/adding/deleting elements in TLV encoding.
      44              :  *
      45              :  * The TLVUpdater is a union of the TLVReader and TLVWriter objects and provides interface methods
      46              :  * for editing/deleting data in an encoding as well as adding new elements to the TLV encoding. The
      47              :  * TLVUpdater object essentially acts like two cursors, one for reading existing encoding and
      48              :  * another for writing (either for copying over existing data or writing new data).
      49              :  *
      50              :  * Semantically, the TLVUpdater object functions like a union of the TLVReader and TLVWriter. The
      51              :  * TLVUpdater methods have more or less similar meanings as similarly named counterparts in
      52              :  * TLVReader/TLVWriter. Where there are differences in the semantics, the differences are clearly
      53              :  * documented in the function's comment section in TLVUpdater.cpp.
      54              :  *
      55              :  * One particularly important note about the TLVUpdater's PutBytes() and PutString() methods is that
      56              :  * it can leave the encoding in a corrupt state with only the element header written when an
      57              :  * overflow occurs. Applications can call GetRemainingFreeLength() to make sure there is
      58              :  * @em approximately enough free space to write the encoding. Note that GetRemainingFreeLength()
      59              :  * only tells you the available free bytes and there is @em no way for the application to know the
      60              :  * length of encoded data that gets written. In the event of an overflow, both PutBytes() and
      61              :  * PutString() will return CHIP_ERROR_BUFFER_TOO_SMALL to the caller.
      62              :  *
      63              :  * Also, note that Next() method is overloaded to both skip the current element and also advance the
      64              :  * internal reader to the next element. Because skipping already encoded elements requires changing
      65              :  * the internal writer's free space state variables to account for the new freed space (made
      66              :  * available by skipping), the application is expected to call Next() on the updater after a Get()
      67              :  * method whose value it doesn't wish to write back (which is equivalent to skipping the current
      68              :  * element).
      69              :  *
      70              :  * @note The application is expected to use the TLVUpdater object atomically from the time it calls
      71              :  * Init() till it calls Finalize(). The same buffer should NOT be used with other TLVWriter objects.
      72              :  *
      73              :  * @note The TLVUpdater currently only supports single static buffers. TLVBackingStore is NOT supported.
      74              :  */
      75              : class DLL_EXPORT TLVUpdater
      76              : {
      77              : public:
      78              :     /**
      79              :      * Initialize a TLVUpdater object to edit a single input buffer.
      80              :      *
      81              :      * On calling this method, the TLV data in the buffer is moved to the end of the
      82              :      * buffer and a private TLVReader object is initialized on this relocated
      83              :      * buffer. A private TLVWriter object is also initialized on the free space that
      84              :      * is now available at the beginning. Applications can use the TLVUpdater object
      85              :      * to parse the TLV data and modify/delete existing elements or add new elements
      86              :      * to the encoding.
      87              :      *
      88              :      * @param[in]   buf     A pointer to a buffer containing the TLV data to be edited.
      89              :      * @param[in]   dataLen The length of the TLV data in the buffer.
      90              :      * @param[in]   maxLen  The total length of the buffer.
      91              :      *
      92              :      * @retval #CHIP_NO_ERROR                  If the method succeeded.
      93              :      * @retval #CHIP_ERROR_INVALID_ARGUMENT    If the buffer address is invalid.
      94              :      * @retval #CHIP_ERROR_BUFFER_TOO_SMALL    If the buffer is too small.
      95              :      *
      96              :      */
      97              :     CHIP_ERROR Init(uint8_t * buf, uint32_t dataLen, uint32_t maxLen);
      98              : 
      99              :     /**
     100              :      * Initialize a TLVUpdater object using a TLVReader.
     101              :      *
     102              :      * On calling this method, TLV data in the buffer pointed to by the TLVReader
     103              :      * is moved from the current read point to the end of the buffer. A new
     104              :      * private TLVReader object is initialized to read from this new location, while
     105              :      * a new private TLVWriter object is initialized to write to the freed up buffer
     106              :      * space.
     107              :      *
     108              :      * Note that if the TLVReader is already positioned "on" an element, it is first
     109              :      * backed-off to the start of that element. Also note that this backing off
     110              :      * works well with container elements, i.e., if the TLVReader was already  used
     111              :      * to call EnterContainer(), then there is nothing to back-off. But if the
     112              :      * TLVReader was positioned on the container element and EnterContainer() was
     113              :      * not yet called, then the TLVReader object is backed-off to the start of the
     114              :      * container head.
     115              :      *
     116              :      * The input TLVReader object will be destroyed before returning and the
     117              :      * application must not make use of the same on return.
     118              :      *
     119              :      * @param[in,out]   aReader     Reference to a TLVReader object that will be
     120              :      *                              destroyed before returning.
     121              :      * @param[in]       freeLen     The length of free space (in bytes) available
     122              :      *                              in the pre-encoded data buffer.
     123              :      *
     124              :      * @retval #CHIP_NO_ERROR                  If the method succeeded.
     125              :      * @retval #CHIP_ERROR_INVALID_ARGUMENT    If the buffer address is invalid.
     126              :      * @retval #CHIP_ERROR_NOT_IMPLEMENTED     If reader was initialized on a chain
     127              :      *                                          of buffers.
     128              :      */
     129              :     CHIP_ERROR Init(TLVReader & aReader, uint32_t freeLen);
     130              : 
     131            6 :     CHIP_ERROR Finalize() { return mUpdaterWriter.Finalize(); }
     132              : 
     133              :     // Common methods
     134              : 
     135              :     /**
     136              :      * Set the Implicit Profile ID for the TLVUpdater object.
     137              :      *
     138              :      * This method sets the implicit profile ID for the TLVUpdater object. When the
     139              :      * updater is asked to encode a new element, if the profile ID of the tag
     140              :      * associated with the new element matches the value of the @p profileId, the
     141              :      * updater will encode the tag in implicit form, thereby omitting the profile ID
     142              :      * in the process.
     143              :      *
     144              :      * @param[in]   profileId   The profile id of tags that should be encoded in
     145              :      *                          implicit form.
     146              :      */
     147              :     void SetImplicitProfileId(uint32_t profileId);
     148              :     uint32_t GetImplicitProfileId() const { return mUpdaterReader.ImplicitProfileId; }
     149              : 
     150              :     /**
     151              :      * Copies the current element from input TLV to output TLV.
     152              :      *
     153              :      * The Move() method copies the current element on which the TLVUpdater's reader
     154              :      * is positioned on, to the TLVUpdater's writer. The application should call
     155              :      * Next() and position the TLVUpdater's reader on an element before calling this
     156              :      * method. Just like the TLVReader::Next() method, if the reader is positioned
     157              :      * on a container element at the time of the call, all the members of the
     158              :      * container will be copied. If the reader is not positioned on any element,
     159              :      * nothing changes on calling this method.
     160              :      *
     161              :      * @retval #CHIP_NO_ERROR              If the TLVUpdater reader was
     162              :      *                                      successfully positioned on a new
     163              :      *                                      element.
     164              :      * @retval #CHIP_END_OF_TLV            If the TLVUpdater's reader is pointing
     165              :      *                                      to end of container.
     166              :      * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
     167              :      *                                      If the TLVIpdater's reader is not
     168              :      *                                      positioned on a valid TLV element.
     169              :      * @retval other                        Returns other error codes returned by
     170              :      *                                      TLVReader::Skip() method.
     171              :      *
     172              :      */
     173              :     CHIP_ERROR Move();
     174              : 
     175              :     /**
     176              :      * Move everything from the TLVUpdater's current read point till end of input
     177              :      * TLV buffer over to output.
     178              :      *
     179              :      * This method supports moving everything from the TLVUpdater's current read
     180              :      * point till the end of the reader buffer over to the TLVUpdater's writer.
     181              :      *
     182              :      * @note This method can be called with the TLVUpdater's reader positioned
     183              :      * anywhere within the input TLV. The reader can also be positioned under
     184              :      * multiple levels of nested containers and this method will still work.
     185              :      *
     186              :      * @note This method also changes the state of the TLVUpdater object to a state
     187              :      * it would be in if the application had painstakingly parsed each element from
     188              :      * the current read point till the end of the input encoding and copied them to
     189              :      * the output TLV.
     190              :      */
     191              :     void MoveUntilEnd();
     192              : 
     193              :     /**
     194              :      * Prepares a TLVUpdater object for reading elements of a container. It also
     195              :      * encodes a start of container object in the output TLV.
     196              :      *
     197              :      * The EnterContainer() method prepares the current TLVUpdater object to begin
     198              :      * reading the member elements of a TLV container (a structure, array or path).
     199              :      * For every call to EnterContainer() applications must make a corresponding
     200              :      * call to ExitContainer().
     201              :      *
     202              :      * When EnterContainer() is called the TLVUpdater's reader must be positioned on
     203              :      * the container element. The method takes as an argument a reference to a
     204              :      * TLVType value which will be used to save the context of the updater while it
     205              :      * is reading the container.
     206              :      *
     207              :      * When the EnterContainer() method returns, the updater is positioned
     208              :      * immediately @em before the first member of the container. Repeatedly calling
     209              :      * Next() will advance the updater through the members of the collection until
     210              :      * the end is reached, at which point the updater will return CHIP_END_OF_TLV.
     211              :      *
     212              :      * Once the application has finished reading a container it can continue reading
     213              :      * the elements after the container by calling the ExitContainer() method.
     214              :      *
     215              :      * @note This method implicitly encodes a start of container element in the
     216              :      * output TLV buffer.
     217              :      *
     218              :      * @param[out] outerContainerType       A reference to a TLVType value that will
     219              :      *                                      receive the context of the updater.
     220              :      *
     221              :      * @retval #CHIP_NO_ERROR              If the method succeeded.
     222              :      * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVUpdater reader is not
     223              :      *                                      positioned on a container element.
     224              :      * @retval other                        Any other CHIP or platform error code
     225              :      *                                      returned by TLVWriter::StartContainer()
     226              :      *                                      or TLVReader::EnterContainer().
     227              :      *
     228              :      */
     229              :     CHIP_ERROR EnterContainer(TLVType & outerContainerType);
     230              : 
     231              :     /**
     232              :      * Completes the reading of a TLV container element and encodes an end of TLV
     233              :      * element in the output TLV.
     234              :      *
     235              :      * The ExitContainer() method restores the state of a TLVUpdater object after a
     236              :      * call to EnterContainer(). For every call to EnterContainer() applications
     237              :      * must make a corresponding call to ExitContainer(), passing the context value
     238              :      * returned by the EnterContainer() method.
     239              :      *
     240              :      * When ExitContainer() returns, the TLVUpdater reader is positioned immediately
     241              :      * before the first element that follows the container in the input TLV. From
     242              :      * this point applications can call Next() to advance through any remaining
     243              :      * elements.
     244              :      *
     245              :      * Once EnterContainer() has been called, applications can call ExitContainer()
     246              :      * on the updater at any point in time, regardless of whether all elements in
     247              :      * the underlying container have been read. Also, note that calling
     248              :      * ExitContainer() before reading all the elements in the container, will result
     249              :      * in the updated container getting truncated in the output TLV.
     250              :      *
     251              :      * @note Any changes made to the configuration of the updater between the calls
     252              :      * to EnterContainer() and ExitContainer() are NOT undone by the call to
     253              :      * ExitContainer(). For example, a change to the implicit profile id
     254              :      * (@p ImplicitProfileId) will not be reversed when a container is exited. Thus
     255              :      * it is the application's responsibility to adjust the configuration
     256              :      * accordingly at the appropriate times.
     257              :      *
     258              :      * @param[in] outerContainerType        The TLVType value that was returned by
     259              :      *                                      the EnterContainer() method.
     260              :      *
     261              :      * @retval #CHIP_NO_ERROR              If the method succeeded.
     262              :      * @retval #CHIP_ERROR_TLV_UNDERRUN    If the underlying TLV encoding ended
     263              :      *                                      prematurely.
     264              :      * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT
     265              :      *                                      If the updater encountered an invalid or
     266              :      *                                      unsupported TLV element type.
     267              :      * @retval #CHIP_ERROR_INVALID_TLV_TAG If the updater encountered a TLV tag in
     268              :      *                                      an invalid context.
     269              :      * @retval other                        Any other CHIP or platform error code
     270              :      *                                      returned by TLVWriter::EndContainer() or
     271              :      *                                      TLVReader::ExitContainer().
     272              :      *
     273              :      */
     274              :     CHIP_ERROR ExitContainer(TLVType outerContainerType);
     275              : 
     276            1 :     void GetReader(TLVReader & containerReader) { containerReader = mUpdaterReader; }
     277              : 
     278              :     // Reader methods
     279              : 
     280              :     /**
     281              :      * Skip the current element and advance the TLVUpdater object to the next
     282              :      * element in the input TLV.
     283              :      *
     284              :      * The Next() method skips the current element in the input TLV and advances the
     285              :      * TLVUpdater's reader to the next element that resides in the same containment
     286              :      * context. In particular, if the reader is positioned at the outer most level
     287              :      * of a TLV encoding, calling Next() will advance it to the next, top most
     288              :      * element. If the reader is positioned within a TLV container element (a
     289              :      * structure, array or path), calling Next() will advance it to the next member
     290              :      * element of the container.
     291              :      *
     292              :      * Since Next() constrains reader motion to the current containment context,
     293              :      * calling Next() when the reader is positioned on a container element will
     294              :      * advance @em over the container, skipping its member elements (and the members
     295              :      * of any nested containers) until it reaches the first element after the
     296              :      * container.
     297              :      *
     298              :      * When there are no further elements within a particular containment context
     299              :      * the Next() method will return a #CHIP_END_OF_TLV error and the position of
     300              :      * the reader will remain unchanged.
     301              :      *
     302              :      * @note The Next() method implicitly skips the current element. Hence, the
     303              :      * TLVUpdater's private writer state variables will be adjusted to account for
     304              :      * the new freed space (made available by skipping). This means that the
     305              :      * application is expected to call Next() on the TLVUpdater object after a Get()
     306              :      * whose value the application does @em not write back (which from the
     307              :      * TLVUpdater's view is equivalent to skipping that element).
     308              :      *
     309              :      * @note Applications are also expected to call Next() when they are at the end
     310              :      * of a container, and want to add new elements there. This is particularly
     311              :      * important in situations where there is a fixed schema. Applications that have
     312              :      * fixed schemas and know where the container end is cannot just add new
     313              :      * elements at the end, because the TLVUpdater writer's state will not reflect
     314              :      * the correct free space available for the Put() operation. Hence, applications
     315              :      * must call Next() (and possibly also test for CHIP_END_OF_TLV) before adding
     316              :      * elements at the end of a container.
     317              :      *
     318              :      * @retval #CHIP_NO_ERROR              If the TLVUpdater reader was
     319              :      *                                      successfully positioned on a new
     320              :      *                                      element.
     321              :      * @retval other                        Returns the CHIP or platform error
     322              :      *                                      codes returned by the TLVReader::Skip()
     323              :      *                                      and TLVReader::Next() method.
     324              :      *
     325              :      */
     326              :     CHIP_ERROR Next();
     327              : 
     328            2 :     CHIP_ERROR Get(bool & v) { return mUpdaterReader.Get(v); }
     329              :     CHIP_ERROR Get(int8_t & v) { return mUpdaterReader.Get(v); }
     330              :     CHIP_ERROR Get(int16_t & v) { return mUpdaterReader.Get(v); }
     331              :     CHIP_ERROR Get(int32_t & v) { return mUpdaterReader.Get(v); }
     332              :     CHIP_ERROR Get(int64_t & v) { return mUpdaterReader.Get(v); }
     333              :     CHIP_ERROR Get(uint8_t & v) { return mUpdaterReader.Get(v); }
     334              :     CHIP_ERROR Get(uint16_t & v) { return mUpdaterReader.Get(v); }
     335              :     CHIP_ERROR Get(uint32_t & v) { return mUpdaterReader.Get(v); }
     336              :     CHIP_ERROR Get(uint64_t & v) { return mUpdaterReader.Get(v); }
     337              :     CHIP_ERROR Get(float & v) { return mUpdaterReader.Get(v); }
     338              :     CHIP_ERROR Get(double & v) { return mUpdaterReader.Get(v); }
     339              :     CHIP_ERROR Get(ByteSpan & v) { return mUpdaterReader.Get(v); }
     340              :     CHIP_ERROR Get(CharSpan & v) { return mUpdaterReader.Get(v); }
     341              : 
     342              :     CHIP_ERROR GetBytes(uint8_t * buf, uint32_t bufSize) { return mUpdaterReader.GetBytes(buf, bufSize); }
     343              :     CHIP_ERROR DupBytes(uint8_t *& buf, uint32_t & dataLen) { return mUpdaterReader.DupBytes(buf, dataLen); }
     344              :     CHIP_ERROR GetString(char * buf, uint32_t bufSize) { return mUpdaterReader.GetString(buf, bufSize); }
     345              :     CHIP_ERROR DupString(char *& buf) { return mUpdaterReader.DupString(buf); }
     346              : 
     347            5 :     TLVType GetType() const { return mUpdaterReader.GetType(); }
     348            5 :     Tag GetTag() const { return mUpdaterReader.GetTag(); }
     349            5 :     uint32_t GetLength() const { return mUpdaterReader.GetLength(); }
     350              :     CHIP_ERROR GetDataPtr(const uint8_t *& data) { return mUpdaterReader.GetDataPtr(data); }
     351              :     CHIP_ERROR VerifyEndOfContainer() { return mUpdaterReader.VerifyEndOfContainer(); }
     352            9 :     TLVType GetContainerType() const { return mUpdaterReader.GetContainerType(); }
     353              :     uint32_t GetLengthRead() const { return mUpdaterReader.GetLengthRead(); }
     354              :     uint32_t GetRemainingLength() const { return mUpdaterReader.GetRemainingLength(); }
     355              : 
     356              :     // Writer methods
     357              :     CHIP_ERROR Put(Tag tag, int8_t v) { return mUpdaterWriter.Put(tag, v); }
     358              :     CHIP_ERROR Put(Tag tag, int16_t v) { return mUpdaterWriter.Put(tag, v); }
     359              :     CHIP_ERROR Put(Tag tag, int32_t v) { return mUpdaterWriter.Put(tag, v); }
     360              :     CHIP_ERROR Put(Tag tag, int64_t v) { return mUpdaterWriter.Put(tag, v); }
     361              :     CHIP_ERROR Put(Tag tag, uint8_t v) { return mUpdaterWriter.Put(tag, v); }
     362              :     CHIP_ERROR Put(Tag tag, uint16_t v) { return mUpdaterWriter.Put(tag, v); }
     363              :     CHIP_ERROR Put(Tag tag, uint32_t v) { return mUpdaterWriter.Put(tag, v); }
     364              :     CHIP_ERROR Put(Tag tag, uint64_t v) { return mUpdaterWriter.Put(tag, v); }
     365              :     CHIP_ERROR Put(Tag tag, int8_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     366              :     CHIP_ERROR Put(Tag tag, int16_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     367              :     CHIP_ERROR Put(Tag tag, int32_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     368              :     CHIP_ERROR Put(Tag tag, int64_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     369              :     CHIP_ERROR Put(Tag tag, uint8_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     370              :     CHIP_ERROR Put(Tag tag, uint16_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     371              :     CHIP_ERROR Put(Tag tag, uint32_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     372              :     CHIP_ERROR Put(Tag tag, uint64_t v, bool preserveSize) { return mUpdaterWriter.Put(tag, v, preserveSize); }
     373              :     CHIP_ERROR Put(Tag tag, float v) { return mUpdaterWriter.Put(tag, v); }
     374              :     CHIP_ERROR Put(Tag tag, double v) { return mUpdaterWriter.Put(tag, v); }
     375           21 :     CHIP_ERROR PutBoolean(Tag tag, bool v) { return mUpdaterWriter.PutBoolean(tag, v); }
     376              :     CHIP_ERROR PutNull(Tag tag) { return mUpdaterWriter.PutNull(tag); }
     377              :     CHIP_ERROR PutBytes(Tag tag, const uint8_t * buf, uint32_t len) { return mUpdaterWriter.PutBytes(tag, buf, len); }
     378              :     CHIP_ERROR PutString(Tag tag, const char * buf) { return mUpdaterWriter.PutString(tag, buf); }
     379              :     CHIP_ERROR PutString(Tag tag, const char * buf, uint32_t len) { return mUpdaterWriter.PutString(tag, buf, len); }
     380              :     CHIP_ERROR CopyElement(TLVReader & reader) { return mUpdaterWriter.CopyElement(reader); }
     381              :     CHIP_ERROR CopyElement(Tag tag, TLVReader & reader) { return mUpdaterWriter.CopyElement(tag, reader); }
     382            7 :     CHIP_ERROR StartContainer(Tag tag, TLVType containerType, TLVType & outerContainerType)
     383              :     {
     384            7 :         return mUpdaterWriter.StartContainer(tag, containerType, outerContainerType);
     385              :     }
     386            7 :     CHIP_ERROR EndContainer(TLVType outerContainerType) { return mUpdaterWriter.EndContainer(outerContainerType); }
     387            6 :     uint32_t GetLengthWritten() { return mUpdaterWriter.GetLengthWritten(); }
     388              :     uint32_t GetRemainingFreeLength() const { return mUpdaterWriter.mRemainingLen; }
     389              : 
     390              : private:
     391              :     void AdjustInternalWriterFreeSpace();
     392              : 
     393              :     TLVWriter mUpdaterWriter;
     394              :     TLVReader mUpdaterReader;
     395              :     const uint8_t * mElementStartAddr;
     396              : };
     397              : 
     398              : } // namespace TLV
     399              : } // namespace chip
        

Generated by: LCOV version 2.0-1