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