Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 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 : #include <lib/core/TLVWriter.h>
19 :
20 : #ifndef __STDC_LIMIT_MACROS
21 : #define __STDC_LIMIT_MACROS
22 : #endif
23 :
24 : #include <stdarg.h>
25 : #include <stdint.h>
26 : #include <stdio.h>
27 : #include <string.h>
28 :
29 : #include <lib/core/CHIPConfig.h>
30 : #include <lib/core/CHIPEncoding.h>
31 : #include <lib/core/CHIPError.h>
32 : #include <lib/core/TLVBackingStore.h>
33 : #include <lib/core/TLVCommon.h>
34 : #include <lib/core/TLVReader.h>
35 : #include <lib/core/TLVTags.h>
36 : #include <lib/core/TLVTypes.h>
37 : #include <lib/support/BufferWriter.h>
38 : #include <lib/support/CHIPMem.h>
39 : #include <lib/support/CodeUtils.h>
40 : #include <lib/support/SafeInt.h>
41 : #include <lib/support/Span.h>
42 : #include <lib/support/logging/Constants.h>
43 : #include <lib/support/logging/TextOnlyLogging.h>
44 : #include <lib/support/utf8.h>
45 : #include <system/SystemConfig.h>
46 :
47 : // Doxygen is confused by the __attribute__ annotation
48 : #ifndef DOXYGEN
49 : #define NO_INLINE __attribute__((noinline))
50 : #endif // DOXYGEN
51 :
52 : // You can enable this block manually to abort on usage of uninitialized writers in
53 : // your codebase. There are no such usages in the SDK (outside of tests).
54 : #if 0
55 : #define ABORT_ON_UNINITIALIZED_IF_ENABLED() VerifyOrDie(IsInitialized() == true)
56 : #else
57 : #define ABORT_ON_UNINITIALIZED_IF_ENABLED() \
58 : do \
59 : { \
60 : } while (0)
61 : #endif
62 :
63 : namespace chip {
64 : namespace TLV {
65 :
66 : using namespace chip::Encoding;
67 :
68 44063 : TLVWriter::TLVWriter() :
69 44063 : ImplicitProfileId(kProfileIdNotSpecified), AppData(nullptr), mBackingStore(nullptr), mBufStart(nullptr), mWritePoint(nullptr),
70 44063 : mRemainingLen(0), mLenWritten(0), mMaxLen(0), mReservedSize(0), mContainerType(kTLVType_NotSpecified), mInitializationCookie(0),
71 44063 : mContainerOpen(false), mCloseContainerReserved(true)
72 44063 : {}
73 :
74 30668 : NO_INLINE void TLVWriter::Init(uint8_t * buf, size_t maxLen)
75 : {
76 : // TODO: Maybe we can just make mMaxLen, mLenWritten, mRemainingLen size_t instead?
77 30668 : uint32_t actualMaxLen = maxLen > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(maxLen);
78 :
79 : // TODO(#30825): Need to ensure a single init path for this complex data.
80 30668 : mInitializationCookie = 0;
81 30668 : mBackingStore = nullptr;
82 30668 : mBufStart = buf;
83 30668 : mWritePoint = buf;
84 30668 : mRemainingLen = actualMaxLen;
85 30668 : mLenWritten = 0;
86 30668 : mMaxLen = actualMaxLen;
87 30668 : mContainerType = kTLVType_NotSpecified;
88 30668 : mReservedSize = 0;
89 30668 : SetContainerOpen(false);
90 30668 : SetCloseContainerReserved(true);
91 :
92 30668 : ImplicitProfileId = kProfileIdNotSpecified;
93 30668 : mInitializationCookie = kExpectedInitializationCookie;
94 30668 : }
95 :
96 20127 : CHIP_ERROR TLVWriter::Init(TLVBackingStore & backingStore, uint32_t maxLen /* = UINT32_MAX */)
97 : {
98 : // TODO(#30825): Need to ensure a single init path for this complex data.
99 20127 : Init(nullptr, maxLen);
100 20127 : mInitializationCookie = 0;
101 :
102 20127 : mBackingStore = &backingStore;
103 20127 : mBufStart = nullptr;
104 20127 : mRemainingLen = 0;
105 20127 : CHIP_ERROR err = mBackingStore->OnInit(*this, mBufStart, mRemainingLen);
106 20127 : if (err != CHIP_NO_ERROR)
107 0 : return err;
108 :
109 20127 : VerifyOrReturnError(mBufStart != nullptr, CHIP_ERROR_INTERNAL);
110 20127 : mWritePoint = mBufStart;
111 20127 : mInitializationCookie = kExpectedInitializationCookie;
112 20127 : return CHIP_NO_ERROR;
113 : }
114 :
115 23653 : CHIP_ERROR TLVWriter::Finalize()
116 : {
117 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
118 :
119 23653 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
120 :
121 23652 : CHIP_ERROR err = CHIP_NO_ERROR;
122 23652 : if (IsContainerOpen())
123 0 : return CHIP_ERROR_TLV_CONTAINER_OPEN;
124 23652 : if (mBackingStore != nullptr)
125 21606 : err = mBackingStore->FinalizeBuffer(*this, mBufStart, static_cast<uint32_t>(mWritePoint - mBufStart));
126 :
127 : // TODO(#30825) The following should be safe, but in some cases (without mBackingStore), there are incremental writes that
128 : // start failing.
129 : #if 0
130 : if (err == CHIP_NO_ERROR)
131 : mInitializationCookie = 0;
132 : #endif
133 :
134 23652 : return err;
135 : }
136 :
137 12392 : CHIP_ERROR TLVWriter::ReserveBuffer(uint32_t aBufferSize)
138 : {
139 12392 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
140 12391 : VerifyOrReturnError(mRemainingLen >= aBufferSize, CHIP_ERROR_NO_MEMORY);
141 :
142 12379 : if (mBackingStore)
143 : {
144 12366 : VerifyOrReturnError(mBackingStore->GetNewBufferWillAlwaysFail(), CHIP_ERROR_INCORRECT_STATE);
145 : }
146 12378 : mReservedSize += aBufferSize;
147 12378 : mRemainingLen -= aBufferSize;
148 12378 : return CHIP_NO_ERROR;
149 : }
150 :
151 10515 : CHIP_ERROR TLVWriter::PutBoolean(Tag tag, bool v)
152 : {
153 10515 : return WriteElementHead((v) ? TLVElementType::BooleanTrue : TLVElementType::BooleanFalse, tag, 0);
154 : }
155 :
156 27579 : CHIP_ERROR TLVWriter::Put(Tag tag, uint8_t v)
157 : {
158 27579 : return Put(tag, static_cast<uint64_t>(v));
159 : }
160 :
161 3 : CHIP_ERROR TLVWriter::Put(Tag tag, uint8_t v, bool preserveSize)
162 : {
163 3 : if (preserveSize)
164 3 : return WriteElementHead(TLVElementType::UInt8, tag, v);
165 0 : return Put(tag, v);
166 : }
167 :
168 22146 : CHIP_ERROR TLVWriter::Put(Tag tag, uint16_t v)
169 : {
170 22146 : return Put(tag, static_cast<uint64_t>(v));
171 : }
172 :
173 3 : CHIP_ERROR TLVWriter::Put(Tag tag, uint16_t v, bool preserveSize)
174 : {
175 3 : if (preserveSize)
176 3 : return WriteElementHead(TLVElementType::UInt16, tag, v);
177 0 : return Put(tag, v);
178 : }
179 :
180 38098 : CHIP_ERROR TLVWriter::Put(Tag tag, uint32_t v)
181 : {
182 38098 : return Put(tag, static_cast<uint64_t>(v));
183 : }
184 :
185 3 : CHIP_ERROR TLVWriter::Put(Tag tag, uint32_t v, bool preserveSize)
186 : {
187 3 : if (preserveSize)
188 3 : return WriteElementHead(TLVElementType::UInt32, tag, v);
189 0 : return Put(tag, v);
190 : }
191 :
192 101087 : CHIP_ERROR TLVWriter::Put(Tag tag, uint64_t v)
193 : {
194 : TLVElementType elemType;
195 101087 : if (v <= UINT8_MAX)
196 73376 : elemType = TLVElementType::UInt8;
197 27711 : else if (v <= UINT16_MAX)
198 7852 : elemType = TLVElementType::UInt16;
199 19859 : else if (v <= UINT32_MAX)
200 18067 : elemType = TLVElementType::UInt32;
201 : else
202 1792 : elemType = TLVElementType::UInt64;
203 101087 : return WriteElementHead(elemType, tag, v);
204 : }
205 :
206 5 : CHIP_ERROR TLVWriter::Put(Tag tag, uint64_t v, bool preserveSize)
207 : {
208 5 : if (preserveSize)
209 3 : return WriteElementHead(TLVElementType::UInt64, tag, v);
210 2 : return Put(tag, v);
211 : }
212 :
213 20 : CHIP_ERROR TLVWriter::Put(Tag tag, int8_t v)
214 : {
215 20 : return Put(tag, static_cast<int64_t>(v));
216 : }
217 :
218 3 : CHIP_ERROR TLVWriter::Put(Tag tag, int8_t v, bool preserveSize)
219 : {
220 3 : if (preserveSize)
221 3 : return WriteElementHead(TLVElementType::Int8, tag, static_cast<uint8_t>(v));
222 0 : return Put(tag, v);
223 : }
224 :
225 53 : CHIP_ERROR TLVWriter::Put(Tag tag, int16_t v)
226 : {
227 53 : return Put(tag, static_cast<int64_t>(v));
228 : }
229 :
230 5 : CHIP_ERROR TLVWriter::Put(Tag tag, int16_t v, bool preserveSize)
231 : {
232 5 : if (preserveSize)
233 5 : return WriteElementHead(TLVElementType::Int16, tag, static_cast<uint16_t>(v));
234 0 : return Put(tag, v);
235 : }
236 :
237 1447 : CHIP_ERROR TLVWriter::Put(Tag tag, int32_t v)
238 : {
239 1447 : return Put(tag, static_cast<int64_t>(v));
240 : }
241 :
242 3 : CHIP_ERROR TLVWriter::Put(Tag tag, int32_t v, bool preserveSize)
243 : {
244 3 : if (preserveSize)
245 3 : return WriteElementHead(TLVElementType::Int32, tag, static_cast<uint32_t>(v));
246 0 : return Put(tag, v);
247 : }
248 :
249 1642 : CHIP_ERROR TLVWriter::Put(Tag tag, int64_t v)
250 : {
251 : TLVElementType elemType;
252 1642 : if (v >= INT8_MIN && v <= INT8_MAX)
253 1195 : elemType = TLVElementType::Int8;
254 447 : else if (v >= INT16_MIN && v <= INT16_MAX)
255 16 : elemType = TLVElementType::Int16;
256 431 : else if (v >= INT32_MIN && v <= INT32_MAX)
257 409 : elemType = TLVElementType::Int32;
258 : else
259 22 : elemType = TLVElementType::Int64;
260 1642 : return WriteElementHead(elemType, tag, static_cast<uint64_t>(v));
261 : }
262 :
263 9 : CHIP_ERROR TLVWriter::Put(Tag tag, int64_t v, bool preserveSize)
264 : {
265 9 : if (preserveSize)
266 5 : return WriteElementHead(TLVElementType::Int64, tag, static_cast<uint64_t>(v));
267 4 : return Put(tag, v);
268 : }
269 :
270 569 : CHIP_ERROR TLVWriter::Put(Tag tag, const float v)
271 : {
272 : uint32_t u32;
273 569 : memcpy(&u32, &v, sizeof(u32));
274 569 : return WriteElementHead(TLVElementType::FloatingPointNumber32, tag, u32);
275 : }
276 :
277 584 : CHIP_ERROR TLVWriter::Put(Tag tag, const double v)
278 : {
279 : uint64_t u64;
280 584 : memcpy(&u64, &v, sizeof(u64));
281 584 : return WriteElementHead(TLVElementType::FloatingPointNumber64, tag, u64);
282 : }
283 :
284 9137 : CHIP_ERROR TLVWriter::Put(Tag tag, ByteSpan data)
285 : {
286 9137 : VerifyOrReturnError(CanCastTo<uint32_t>(data.size()), CHIP_ERROR_MESSAGE_TOO_LONG);
287 9137 : return PutBytes(tag, data.data(), static_cast<uint32_t>(data.size()));
288 : }
289 :
290 9806 : CHIP_ERROR TLVWriter::PutBytes(Tag tag, const uint8_t * buf, uint32_t len)
291 : {
292 9806 : return WriteElementWithData(kTLVType_ByteString, tag, buf, len);
293 : }
294 :
295 878 : CHIP_ERROR TLVWriter::PutString(Tag tag, const char * buf)
296 : {
297 878 : size_t len = strlen(buf);
298 878 : if (!CanCastTo<uint32_t>(len))
299 : {
300 0 : return CHIP_ERROR_INVALID_ARGUMENT;
301 : }
302 878 : return PutString(tag, buf, static_cast<uint32_t>(len));
303 : }
304 :
305 2011 : CHIP_ERROR TLVWriter::PutString(Tag tag, const char * buf, uint32_t len)
306 : {
307 : #if CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_WRITE
308 : // Spec requirement: A.11.2. UTF-8 and Octet Strings
309 : //
310 : // For UTF-8 strings, the value octets SHALL encode a valid
311 : // UTF-8 character (code points) sequence.
312 : //
313 : // Senders SHALL NOT include a terminating null character to
314 : // mark the end of a string.
315 :
316 2011 : if (!Utf8::IsValid(CharSpan(buf, len)))
317 : {
318 0 : return CHIP_ERROR_INVALID_UTF8;
319 : }
320 :
321 2011 : if ((len > 0) && (buf[len - 1] == 0))
322 : {
323 0 : return CHIP_ERROR_INVALID_TLV_CHAR_STRING;
324 : }
325 : #endif // CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ
326 :
327 2011 : return WriteElementWithData(kTLVType_UTF8String, tag, reinterpret_cast<const uint8_t *>(buf), len);
328 : }
329 :
330 901 : CHIP_ERROR TLVWriter::PutString(Tag tag, CharSpan str)
331 : {
332 901 : if (!CanCastTo<uint32_t>(str.size()))
333 : {
334 1 : return CHIP_ERROR_INVALID_ARGUMENT;
335 : }
336 :
337 900 : return PutString(tag, str.data(), static_cast<uint32_t>(str.size()));
338 : }
339 :
340 4 : CHIP_ERROR TLVWriter::PutStringF(Tag tag, const char * fmt, ...)
341 : {
342 : CHIP_ERROR err;
343 : va_list ap;
344 :
345 4 : va_start(ap, fmt);
346 :
347 4 : err = VPutStringF(tag, fmt, ap);
348 :
349 4 : va_end(ap);
350 :
351 4 : return err;
352 : }
353 :
354 : #if CONFIG_HAVE_VCBPRINTF
355 : // We have a variant of the printf function that takes a callback that
356 : // emits a single character. The callback performs a function
357 : // identical to putchar.
358 :
359 : void TLVWriter::TLVWriterPutcharCB(uint8_t c, void * appState)
360 : {
361 : TLVWriter * w = static_cast<TLVWriter *>(appState);
362 : w->WriteData(&c, sizeof(c));
363 : }
364 : #endif
365 :
366 4 : CHIP_ERROR TLVWriter::VPutStringF(Tag tag, const char * fmt, va_list ap)
367 : {
368 : va_list aq;
369 : size_t dataLen;
370 4 : CHIP_ERROR err = CHIP_NO_ERROR;
371 : TLVFieldSize lenFieldSize;
372 : #if !CONFIG_HAVE_VCBPRINTF
373 : char * tmpBuf;
374 : #endif
375 4 : va_copy(aq, ap);
376 :
377 4 : dataLen = static_cast<size_t>(vsnprintf(nullptr, 0, fmt, aq));
378 :
379 4 : va_end(aq);
380 :
381 4 : if (!CanCastTo<uint32_t>(dataLen))
382 : {
383 0 : return CHIP_ERROR_INVALID_ARGUMENT;
384 : }
385 :
386 4 : if (dataLen <= UINT8_MAX)
387 4 : lenFieldSize = kTLVFieldSize_1Byte;
388 0 : else if (dataLen <= UINT16_MAX)
389 0 : lenFieldSize = kTLVFieldSize_2Byte;
390 : else
391 0 : lenFieldSize = kTLVFieldSize_4Byte;
392 :
393 : // write length.
394 8 : err = WriteElementHead(
395 4 : static_cast<TLVElementType>(static_cast<uint8_t>(kTLVType_UTF8String) | static_cast<uint8_t>(lenFieldSize)), tag, dataLen);
396 4 : SuccessOrExit(err);
397 :
398 3 : VerifyOrExit((mLenWritten + dataLen) <= mMaxLen, err = CHIP_ERROR_BUFFER_TOO_SMALL);
399 :
400 : // write data
401 : #if CONFIG_HAVE_VCBPRINTF
402 :
403 : va_copy(aq, ap);
404 :
405 : vcbprintf(TLVWriterPutcharCB, this, dataLen, fmt, aq);
406 :
407 : va_end(aq);
408 : #else // CONFIG_HAVE_VCBPRINTF
409 :
410 3 : tmpBuf = static_cast<char *>(chip::Platform::MemoryAlloc(dataLen + 1));
411 3 : VerifyOrExit(tmpBuf != nullptr, err = CHIP_ERROR_NO_MEMORY);
412 :
413 3 : va_copy(aq, ap);
414 :
415 3 : vsnprintf(tmpBuf, dataLen + 1, fmt, aq);
416 :
417 3 : va_end(aq);
418 :
419 3 : err = WriteData(reinterpret_cast<uint8_t *>(tmpBuf), static_cast<uint32_t>(dataLen));
420 3 : chip::Platform::MemoryFree(tmpBuf);
421 :
422 : #endif // CONFIG_HAVE_VCBPRINTF
423 :
424 4 : exit:
425 :
426 4 : return err;
427 : }
428 :
429 6915 : CHIP_ERROR TLVWriter::PutNull(Tag tag)
430 : {
431 6915 : return WriteElementHead(TLVElementType::Null, tag, 0);
432 : }
433 :
434 10489 : CHIP_ERROR TLVWriter::CopyElement(TLVReader & reader)
435 : {
436 10489 : return CopyElement(reader.GetTag(), reader);
437 : }
438 :
439 : const size_t kTLVCopyChunkSize = 16;
440 :
441 24415 : CHIP_ERROR TLVWriter::CopyElement(Tag tag, TLVReader & reader)
442 : {
443 24415 : TLVElementType elemType = reader.ElementType();
444 24415 : uint64_t elemLenOrVal = reader.mElemLenOrVal;
445 : TLVReader readerHelper; // used to figure out the length of the element and read data of the element
446 : uint32_t copyDataLen;
447 : uint8_t chunk[kTLVCopyChunkSize];
448 :
449 24415 : VerifyOrReturnError(elemType != TLVElementType::NotSpecified && elemType != TLVElementType::EndOfContainer,
450 : CHIP_ERROR_INCORRECT_STATE);
451 :
452 : // Initialize the helper
453 24413 : readerHelper.Init(reader);
454 :
455 : // Skip to the end of the element.
456 24413 : ReturnErrorOnFailure(reader.Skip());
457 :
458 : // Compute the amount of value data to copy from the reader.
459 24413 : copyDataLen = reader.GetLengthRead() - readerHelper.GetLengthRead();
460 :
461 : // Write the head of the new element with the same type and length/value, but using the
462 : // specified tag.
463 24413 : ReturnErrorOnFailure(WriteElementHead(elemType, tag, elemLenOrVal));
464 :
465 45106 : while (copyDataLen > 0)
466 : {
467 21155 : uint32_t chunkSize = copyDataLen > kTLVCopyChunkSize ? kTLVCopyChunkSize : copyDataLen;
468 21155 : ReturnErrorOnFailure(readerHelper.ReadData(chunk, chunkSize));
469 :
470 21155 : ReturnErrorOnFailure(WriteData(chunk, chunkSize));
471 :
472 20802 : copyDataLen -= chunkSize;
473 : }
474 :
475 23951 : return CHIP_NO_ERROR;
476 : }
477 :
478 1641 : CHIP_ERROR TLVWriter::OpenContainer(Tag tag, TLVType containerType, TLVWriter & containerWriter)
479 : {
480 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
481 :
482 1641 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
483 1640 : CHIP_ERROR err = CHIP_NO_ERROR;
484 :
485 1640 : VerifyOrReturnError(TLVTypeIsContainer(containerType), CHIP_ERROR_WRONG_TLV_TYPE);
486 :
487 1639 : if (IsCloseContainerReserved())
488 : {
489 1636 : VerifyOrReturnError(mMaxLen >= kEndOfContainerMarkerSize, CHIP_ERROR_BUFFER_TOO_SMALL);
490 1631 : mMaxLen -= kEndOfContainerMarkerSize;
491 : }
492 1634 : err = WriteElementHead(static_cast<TLVElementType>(containerType), tag, 0);
493 :
494 1634 : if (err != CHIP_NO_ERROR)
495 : {
496 : // undo the space reservation, as the container is not actually open
497 4 : if (IsCloseContainerReserved())
498 4 : mMaxLen += kEndOfContainerMarkerSize;
499 :
500 4 : return err;
501 : }
502 :
503 : // TODO(#30825): Clean-up this separate init path path.
504 1630 : containerWriter.mBackingStore = mBackingStore;
505 1630 : containerWriter.mBufStart = mBufStart;
506 1630 : containerWriter.mWritePoint = mWritePoint;
507 1630 : containerWriter.mRemainingLen = mRemainingLen;
508 1630 : containerWriter.mLenWritten = 0;
509 1630 : containerWriter.mMaxLen = mMaxLen - mLenWritten;
510 1630 : containerWriter.mContainerType = containerType;
511 1630 : containerWriter.SetContainerOpen(false);
512 1630 : containerWriter.SetCloseContainerReserved(IsCloseContainerReserved());
513 1630 : containerWriter.ImplicitProfileId = ImplicitProfileId;
514 1630 : containerWriter.mInitializationCookie = kExpectedInitializationCookie;
515 :
516 1630 : SetContainerOpen(true);
517 :
518 1630 : return CHIP_NO_ERROR;
519 : }
520 :
521 1615 : CHIP_ERROR TLVWriter::CloseContainer(TLVWriter & containerWriter)
522 : {
523 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
524 :
525 1615 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
526 :
527 1614 : if (!TLVTypeIsContainer(containerWriter.mContainerType))
528 2 : return CHIP_ERROR_INCORRECT_STATE;
529 :
530 1612 : if (containerWriter.IsContainerOpen())
531 1 : return CHIP_ERROR_TLV_CONTAINER_OPEN;
532 :
533 1611 : mBackingStore = containerWriter.mBackingStore;
534 1611 : mBufStart = containerWriter.mBufStart;
535 1611 : mWritePoint = containerWriter.mWritePoint;
536 1611 : mRemainingLen = containerWriter.mRemainingLen;
537 1611 : mLenWritten += containerWriter.mLenWritten;
538 :
539 1611 : if (IsCloseContainerReserved())
540 1608 : mMaxLen += kEndOfContainerMarkerSize;
541 :
542 1611 : SetContainerOpen(false);
543 :
544 : // Reset the container writer so that it can't accidentally be used again.
545 1611 : containerWriter.Init(static_cast<uint8_t *>(nullptr), 0);
546 :
547 1611 : return WriteElementHead(TLVElementType::EndOfContainer, AnonymousTag(), 0);
548 : }
549 :
550 80605 : CHIP_ERROR TLVWriter::StartContainer(Tag tag, TLVType containerType, TLVType & outerContainerType)
551 : {
552 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
553 :
554 80605 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
555 80604 : CHIP_ERROR err = CHIP_NO_ERROR;
556 :
557 80604 : VerifyOrReturnError(TLVTypeIsContainer(containerType), CHIP_ERROR_WRONG_TLV_TYPE);
558 :
559 80603 : if (IsCloseContainerReserved())
560 : {
561 80590 : VerifyOrReturnError(mMaxLen >= kEndOfContainerMarkerSize, CHIP_ERROR_BUFFER_TOO_SMALL);
562 80589 : mMaxLen -= kEndOfContainerMarkerSize;
563 : }
564 :
565 80602 : err = WriteElementHead(static_cast<TLVElementType>(containerType), tag, 0);
566 80602 : if (err != CHIP_NO_ERROR)
567 : {
568 : // undo the space reservation, as the container is not actually open
569 175 : if (IsCloseContainerReserved())
570 175 : mMaxLen += kEndOfContainerMarkerSize;
571 :
572 175 : return err;
573 : }
574 80427 : outerContainerType = mContainerType;
575 80427 : mContainerType = containerType;
576 :
577 80427 : SetContainerOpen(false);
578 :
579 80427 : return CHIP_NO_ERROR;
580 : }
581 :
582 75581 : CHIP_ERROR TLVWriter::EndContainer(TLVType outerContainerType)
583 : {
584 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
585 :
586 75581 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
587 :
588 75580 : if (!TLVTypeIsContainer(mContainerType))
589 2 : return CHIP_ERROR_INCORRECT_STATE;
590 :
591 75578 : mContainerType = outerContainerType;
592 :
593 75578 : if (IsCloseContainerReserved())
594 75565 : mMaxLen += kEndOfContainerMarkerSize;
595 :
596 75578 : return WriteElementHead(TLVElementType::EndOfContainer, AnonymousTag(), 0);
597 : }
598 :
599 4 : CHIP_ERROR TLVWriter::PutPreEncodedContainer(Tag tag, TLVType containerType, const uint8_t * data, uint32_t dataLen)
600 : {
601 4 : if (!TLVTypeIsContainer(containerType))
602 1 : return CHIP_ERROR_INVALID_ARGUMENT;
603 :
604 3 : CHIP_ERROR err = WriteElementHead(static_cast<TLVElementType>(containerType), tag, 0);
605 3 : if (err != CHIP_NO_ERROR)
606 1 : return err;
607 :
608 2 : return WriteData(data, dataLen);
609 : }
610 :
611 2 : CHIP_ERROR TLVWriter::CopyContainer(TLVReader & container)
612 : {
613 2 : return CopyContainer(container.GetTag(), container);
614 : }
615 :
616 3 : CHIP_ERROR TLVWriter::CopyContainer(Tag tag, TLVReader & container)
617 : {
618 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
619 :
620 3 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
621 :
622 : // NOTE: This function MUST be used with a TVLReader that is reading from a contiguous buffer.
623 1 : if (container.mBackingStore != nullptr)
624 0 : return CHIP_ERROR_INVALID_ARGUMENT;
625 :
626 : CHIP_ERROR err;
627 : TLVType containerType, outerContainerType;
628 : const uint8_t * containerStart;
629 :
630 1 : containerType = container.GetType();
631 :
632 1 : err = container.EnterContainer(outerContainerType);
633 1 : if (err != CHIP_NO_ERROR)
634 0 : return err;
635 :
636 1 : containerStart = container.GetReadPoint();
637 :
638 1 : err = container.ExitContainer(outerContainerType);
639 1 : if (err != CHIP_NO_ERROR)
640 0 : return err;
641 :
642 1 : return PutPreEncodedContainer(tag, containerType, containerStart,
643 1 : static_cast<uint32_t>(container.GetReadPoint() - containerStart));
644 : }
645 :
646 2 : CHIP_ERROR TLVWriter::CopyContainer(Tag tag, const uint8_t * encodedContainer, uint16_t encodedContainerLen)
647 : {
648 2 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
649 :
650 : TLVReader reader;
651 :
652 1 : reader.Init(encodedContainer, encodedContainerLen);
653 :
654 1 : ReturnErrorOnFailure(reader.Next());
655 :
656 1 : ReturnErrorOnFailure(PutPreEncodedContainer(tag, reader.GetType(), reader.GetReadPoint(), reader.GetRemainingLength()));
657 :
658 1 : return CHIP_NO_ERROR;
659 : }
660 :
661 316998 : CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_t lenOrVal)
662 : {
663 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
664 :
665 316998 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
666 316975 : if (IsContainerOpen())
667 0 : return CHIP_ERROR_TLV_CONTAINER_OPEN;
668 :
669 : uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes
670 316975 : uint8_t * p = stagingBuf;
671 316975 : uint32_t tagNum = TagNumFromTag(tag);
672 :
673 316975 : if (IsSpecialTag(tag))
674 : {
675 312844 : if (tagNum <= Tag::kContextTagMaxNum)
676 : {
677 169575 : if (mContainerType != kTLVType_Structure && mContainerType != kTLVType_List)
678 0 : return CHIP_ERROR_INVALID_TLV_TAG;
679 :
680 169575 : Write8(p, TLVTagControl::ContextSpecific | elemType);
681 169575 : Write8(p, static_cast<uint8_t>(tagNum));
682 : }
683 : else
684 : {
685 143269 : if (elemType != TLVElementType::EndOfContainer && mContainerType != kTLVType_NotSpecified &&
686 38711 : mContainerType != kTLVType_Array && mContainerType != kTLVType_List)
687 0 : return CHIP_ERROR_INVALID_TLV_TAG;
688 :
689 143269 : Write8(p, TLVTagControl::Anonymous | elemType);
690 : }
691 : }
692 : else
693 : {
694 4131 : uint32_t profileId = ProfileIdFromTag(tag);
695 :
696 4131 : if (mContainerType != kTLVType_NotSpecified && mContainerType != kTLVType_Structure && mContainerType != kTLVType_List)
697 0 : return CHIP_ERROR_INVALID_TLV_TAG;
698 :
699 4131 : if (profileId == kCommonProfileId)
700 : {
701 400 : if (tagNum < 65536)
702 : {
703 12 : Write8(p, TLVTagControl::CommonProfile_2Bytes | elemType);
704 12 : LittleEndian::Write16(p, static_cast<uint16_t>(tagNum));
705 : }
706 : else
707 : {
708 388 : Write8(p, TLVTagControl::CommonProfile_4Bytes | elemType);
709 388 : LittleEndian::Write32(p, tagNum);
710 : }
711 : }
712 3731 : else if (profileId == ImplicitProfileId)
713 : {
714 2027 : if (tagNum < 65536)
715 : {
716 850 : Write8(p, TLVTagControl::ImplicitProfile_2Bytes | elemType);
717 850 : LittleEndian::Write16(p, static_cast<uint16_t>(tagNum));
718 : }
719 : else
720 : {
721 1177 : Write8(p, TLVTagControl::ImplicitProfile_4Bytes | elemType);
722 1177 : LittleEndian::Write32(p, tagNum);
723 : }
724 : }
725 : else
726 : {
727 1704 : uint16_t vendorId = static_cast<uint16_t>(profileId >> 16);
728 1704 : uint16_t profileNum = static_cast<uint16_t>(profileId);
729 :
730 1704 : if (tagNum < 65536)
731 : {
732 1689 : Write8(p, TLVTagControl::FullyQualified_6Bytes | elemType);
733 1689 : LittleEndian::Write16(p, vendorId);
734 1689 : LittleEndian::Write16(p, profileNum);
735 1689 : LittleEndian::Write16(p, static_cast<uint16_t>(tagNum));
736 : }
737 : else
738 : {
739 15 : Write8(p, TLVTagControl::FullyQualified_8Bytes | elemType);
740 15 : LittleEndian::Write16(p, vendorId);
741 15 : LittleEndian::Write16(p, profileNum);
742 15 : LittleEndian::Write32(p, tagNum);
743 : }
744 : }
745 : }
746 :
747 316975 : switch (GetTLVFieldSize(elemType))
748 : {
749 187529 : case kTLVFieldSize_0Byte:
750 187529 : break;
751 93145 : case kTLVFieldSize_1Byte:
752 93145 : Write8(p, static_cast<uint8_t>(lenOrVal));
753 93145 : break;
754 12913 : case kTLVFieldSize_2Byte:
755 12913 : LittleEndian::Write16(p, static_cast<uint16_t>(lenOrVal));
756 12913 : break;
757 19048 : case kTLVFieldSize_4Byte:
758 19048 : LittleEndian::Write32(p, static_cast<uint32_t>(lenOrVal));
759 19048 : break;
760 4340 : case kTLVFieldSize_8Byte:
761 4340 : LittleEndian::Write64(p, lenOrVal);
762 4340 : break;
763 : }
764 :
765 316975 : uint32_t bytesStaged = static_cast<uint32_t>(p - stagingBuf);
766 316975 : VerifyOrDie(bytesStaged <= sizeof(stagingBuf));
767 316975 : return WriteData(stagingBuf, bytesStaged);
768 : }
769 :
770 11817 : CHIP_ERROR TLVWriter::WriteElementWithData(TLVType type, Tag tag, const uint8_t * data, uint32_t dataLen)
771 : {
772 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
773 :
774 11817 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
775 11813 : if (static_cast<uint64_t>(type) & kTLVTypeSizeMask)
776 : {
777 : // We won't be able to recover this type properly!
778 0 : return CHIP_ERROR_INVALID_ARGUMENT;
779 : }
780 :
781 : TLVFieldSize lenFieldSize;
782 :
783 11813 : if (dataLen <= UINT8_MAX)
784 8447 : lenFieldSize = kTLVFieldSize_1Byte;
785 3366 : else if (dataLen <= UINT16_MAX)
786 3366 : lenFieldSize = kTLVFieldSize_2Byte;
787 : else
788 0 : lenFieldSize = kTLVFieldSize_4Byte;
789 :
790 11813 : CHIP_ERROR err = WriteElementHead(static_cast<TLVElementType>(static_cast<uint8_t>(type) | static_cast<uint8_t>(lenFieldSize)),
791 : tag, dataLen);
792 11813 : if (err != CHIP_NO_ERROR)
793 33 : return err;
794 :
795 11780 : return WriteData(data, dataLen);
796 : }
797 :
798 349915 : CHIP_ERROR TLVWriter::WriteData(const uint8_t * p, uint32_t len)
799 : {
800 : ABORT_ON_UNINITIALIZED_IF_ENABLED();
801 :
802 349915 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
803 349915 : VerifyOrReturnError((mLenWritten + len) <= mMaxLen, CHIP_ERROR_BUFFER_TOO_SMALL);
804 :
805 694994 : while (len > 0)
806 : {
807 347474 : if (mRemainingLen == 0)
808 : {
809 3084 : VerifyOrReturnError(mBackingStore != nullptr, CHIP_ERROR_NO_MEMORY);
810 :
811 3084 : VerifyOrReturnError(CanCastTo<uint32_t>(mWritePoint - mBufStart), CHIP_ERROR_INCORRECT_STATE);
812 3084 : ReturnErrorOnFailure(mBackingStore->FinalizeBuffer(*this, mBufStart, static_cast<uint32_t>(mWritePoint - mBufStart)));
813 :
814 3084 : ReturnErrorOnFailure(mBackingStore->GetNewBuffer(*this, mBufStart, mRemainingLen));
815 879 : VerifyOrReturnError(mRemainingLen > 0, CHIP_ERROR_NO_MEMORY);
816 :
817 879 : mWritePoint = mBufStart;
818 :
819 879 : if (mRemainingLen > (mMaxLen - mLenWritten))
820 0 : mRemainingLen = (mMaxLen - mLenWritten);
821 : }
822 :
823 345269 : uint32_t writeLen = len;
824 345269 : if (writeLen > mRemainingLen)
825 2738 : writeLen = mRemainingLen;
826 :
827 345269 : memmove(mWritePoint, p, writeLen);
828 345269 : mWritePoint += writeLen;
829 345269 : mRemainingLen -= writeLen;
830 345269 : mLenWritten += writeLen;
831 345269 : p += writeLen;
832 345269 : len -= writeLen;
833 : }
834 :
835 347520 : return CHIP_NO_ERROR;
836 : }
837 :
838 : } // namespace TLV
839 : } // namespace chip
|