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
|