Line data Source code
1 : /*
2 : * Copyright (c) 2021-2024 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #include <app/AttributeValueEncoder.h>
18 :
19 : namespace chip {
20 : namespace app {
21 :
22 : namespace {
23 :
24 : constexpr uint32_t kEndOfListByteCount = 1;
25 : // 2 bytes: one to end the AttributeDataIB and one to end the AttributeReportIB.
26 : constexpr uint32_t kEndOfAttributeReportIBByteCount = 2;
27 : constexpr TLV::TLVType kAttributeDataIBType = TLV::kTLVType_Structure;
28 :
29 : } // anonymous namespace
30 :
31 1980 : CHIP_ERROR AttributeValueEncoder::EnsureListStarted()
32 : {
33 1980 : VerifyOrDie(mCurrentEncodingListIndex == kInvalidListIndex);
34 :
35 1980 : mEncodingInitialList = (mEncodeState.CurrentEncodingListIndex() == kInvalidListIndex);
36 1980 : if (mEncodingInitialList)
37 : {
38 : // Clear mAllowPartialData flag here since this encode procedure is not atomic.
39 : // The most common error in this function is CHIP_ERROR_NO_MEMORY / CHIP_ERROR_BUFFER_TOO_SMALL, just revert and try
40 : // next time is ok.
41 1839 : mEncodeState.SetAllowPartialData(false);
42 :
43 : AttributeReportBuilder builder;
44 :
45 1839 : mPath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
46 1839 : ReturnErrorOnFailure(builder.PrepareAttribute(mAttributeReportIBsBuilder, mPath, mDataVersion));
47 :
48 1741 : auto * attributeDataWriter = mAttributeReportIBsBuilder.GetAttributeReport().GetAttributeData().GetWriter();
49 : TLV::TLVType outerType;
50 1741 : ReturnErrorOnFailure(
51 : attributeDataWriter->StartContainer(TLV::ContextTag(AttributeDataIB::Tag::kData), TLV::kTLVType_Array, outerType));
52 1731 : VerifyOrDie(outerType == kAttributeDataIBType);
53 :
54 : // Instead of reserving hardcoded amounts, we could checkpoint the
55 : // writer, encode array end and FinishAttribute, check that this fits,
56 : // measure how much the writer advanced, then restore the checkpoint,
57 : // reserve the measured value, and save it. But that's probably more
58 : // cycles than just reserving this known constant.
59 1731 : ReturnErrorOnFailure(
60 : mAttributeReportIBsBuilder.GetWriter()->ReserveBuffer(kEndOfAttributeReportIBByteCount + kEndOfListByteCount));
61 :
62 1718 : mEncodeState.SetCurrentEncodingListIndex(0);
63 : }
64 : else
65 : {
66 : // For all elements in the list, a report with append operation will be generated. This will not be changed during encoding
67 : // of each report since the users cannot access mPath.
68 141 : mPath.mListOp = ConcreteDataAttributePath::ListOperation::AppendItem;
69 : }
70 :
71 1859 : mCurrentEncodingListIndex = 0;
72 :
73 : // After encoding the initial list start, the remaining items are atomically encoded into the buffer. Tell report engine to not
74 : // revert partial data.
75 1859 : mEncodeState.SetAllowPartialData(true);
76 :
77 1859 : return CHIP_NO_ERROR;
78 : }
79 :
80 1859 : void AttributeValueEncoder::EnsureListEnded()
81 : {
82 1859 : if (!mEncodingInitialList)
83 : {
84 : // Nothing to do.
85 141 : return;
86 : }
87 :
88 : // Unreserve the space we reserved just for this. Crash if anything here
89 : // fails, because that would mean that we've corrupted our data, and since
90 : // mEncodeState.mAllowPartialData is true nothing will clean up for us here.
91 1718 : auto * attributeDataWriter = mAttributeReportIBsBuilder.GetAttributeReport().GetAttributeData().GetWriter();
92 1718 : VerifyOrDie(attributeDataWriter->UnreserveBuffer(kEndOfListByteCount + kEndOfAttributeReportIBByteCount) == CHIP_NO_ERROR);
93 1718 : VerifyOrDie(attributeDataWriter->EndContainer(kAttributeDataIBType) == CHIP_NO_ERROR);
94 :
95 : AttributeReportBuilder builder;
96 1718 : VerifyOrDie(builder.FinishAttribute(mAttributeReportIBsBuilder) == CHIP_NO_ERROR);
97 :
98 1718 : if (!mEncodedAtLeastOneListItem)
99 : {
100 : // If we have not managed to encode any list items, we don't actually
101 : // want to output the single "empty list" IB that will then be followed
102 : // by one-IB-per-item in the next packet. Just have the reporting
103 : // engine roll back our entire attribute and put us in the next packet.
104 : //
105 : // If we succeeded at encoding the whole list (i.e. the list is in fact
106 : // empty and we fit in the packet), mAllowPartialData will be ignored,
107 : // so it's safe to set it to false even if encoding succeeded.
108 737 : mEncodeState.SetAllowPartialData(false);
109 : }
110 : }
111 :
112 6041 : bool AttributeValueEncoder::ShouldEncodeListItem(TLV::TLVWriter & aCheckpoint)
113 : {
114 : // EncodeListItem (our caller) must be called after EnsureListStarted(),
115 : // thus mCurrentEncodingListIndex and mEncodeState.mCurrentEncodingListIndex
116 : // are not invalid values.
117 6041 : if (mCurrentEncodingListIndex < mEncodeState.CurrentEncodingListIndex())
118 : {
119 : // We have encoded this element in previous chunks, skip it.
120 606 : mCurrentEncodingListIndex++;
121 606 : return false;
122 : }
123 :
124 5435 : mAttributeReportIBsBuilder.Checkpoint(aCheckpoint);
125 5435 : return true;
126 : }
127 :
128 5435 : void AttributeValueEncoder::PostEncodeListItem(CHIP_ERROR aEncodeStatus, const TLV::TLVWriter & aCheckpoint)
129 : {
130 5435 : if (aEncodeStatus != CHIP_NO_ERROR)
131 : {
132 160 : mAttributeReportIBsBuilder.Rollback(aCheckpoint);
133 160 : return;
134 : }
135 :
136 5275 : mCurrentEncodingListIndex++;
137 5275 : mEncodeState.SetCurrentEncodingListIndex(mCurrentEncodingListIndex);
138 5275 : mEncodedAtLeastOneListItem = true;
139 : }
140 :
141 : } // namespace app
142 : } // namespace chip
|