Skip to content

Commit 9b8fffe

Browse files
authoredJan 17, 2025
Reduce the amount of per-instantiation logic in EncodeListItem. (#37093)
The parts that are not dependent on ItemType can be separate functions.
1 parent ac7f062 commit 9b8fffe

File tree

2 files changed

+48
-23
lines changed

2 files changed

+48
-23
lines changed
 

‎src/app/AttributeValueEncoder.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,34 @@ void AttributeValueEncoder::EnsureListEnded()
109109
}
110110
}
111111

112+
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+
if (mCurrentEncodingListIndex < mEncodeState.CurrentEncodingListIndex())
118+
{
119+
// We have encoded this element in previous chunks, skip it.
120+
mCurrentEncodingListIndex++;
121+
return false;
122+
}
123+
124+
mAttributeReportIBsBuilder.Checkpoint(aCheckpoint);
125+
return true;
126+
}
127+
128+
void AttributeValueEncoder::PostEncodeListItem(CHIP_ERROR aEncodeStatus, const TLV::TLVWriter & aCheckpoint)
129+
{
130+
if (aEncodeStatus != CHIP_NO_ERROR)
131+
{
132+
mAttributeReportIBsBuilder.Rollback(aCheckpoint);
133+
return;
134+
}
135+
136+
mCurrentEncodingListIndex++;
137+
mEncodeState.SetCurrentEncodingListIndex(mCurrentEncodingListIndex);
138+
mEncodedAtLeastOneListItem = true;
139+
}
140+
112141
} // namespace app
113142
} // namespace chip

‎src/app/AttributeValueEncoder.h

+19-23
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,22 @@ class AttributeValueEncoder
5555
VerifyOrReturnError(!mAttributeValueEncoder.mIsFabricFiltered ||
5656
aArg.GetFabricIndex() == mAttributeValueEncoder.AccessingFabricIndex(),
5757
CHIP_NO_ERROR);
58-
return mAttributeValueEncoder.EncodeListItem(aArg, mAttributeValueEncoder.AccessingFabricIndex());
58+
return mAttributeValueEncoder.EncodeListItem(mCheckpoint, aArg, mAttributeValueEncoder.AccessingFabricIndex());
5959
}
6060

6161
template <typename T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
6262
CHIP_ERROR Encode(const T & aArg) const
6363
{
64-
return mAttributeValueEncoder.EncodeListItem(aArg);
64+
return mAttributeValueEncoder.EncodeListItem(mCheckpoint, aArg);
6565
}
6666

6767
private:
6868
AttributeValueEncoder & mAttributeValueEncoder;
69+
// Avoid calling the TLVWriter constructor for every instantiation of
70+
// EncodeListItem. We treat encoding as a const operation, so either
71+
// have to put this on the stack (in which case it's per-instantiation),
72+
// or have it as mutable state.
73+
mutable TLV::TLVWriter mCheckpoint;
6974
};
7075

7176
AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, Access::SubjectDescriptor subjectDescriptor,
@@ -163,25 +168,26 @@ class AttributeValueEncoder
163168
friend class ListEncodeHelper;
164169
friend class TestOnlyAttributeValueEncoderAccessor;
165170

171+
// Returns true if the list item should be encoded. If it should, the
172+
// passed-in TLVWriter will be used to checkpoint the current state of our
173+
// attribute report list builder.
174+
bool ShouldEncodeListItem(TLV::TLVWriter & aCheckpoint);
175+
176+
// Does any cleanup work needed after attempting to encode a list item.
177+
void PostEncodeListItem(CHIP_ERROR aEncodeStatus, const TLV::TLVWriter & aCheckpoint);
178+
166179
// EncodeListItem may be given an extra FabricIndex argument as a second
167180
// arg, or not. Represent that via a parameter pack (which might be
168181
// empty). In practice, for any given ItemType the extra arg is either there
169182
// or not, so we don't get more template explosion due to aExtraArgs.
170183
template <typename ItemType, typename... ExtraArgTypes>
171-
CHIP_ERROR EncodeListItem(const ItemType & aItem, ExtraArgTypes &&... aExtraArgs)
184+
CHIP_ERROR EncodeListItem(TLV::TLVWriter & aCheckpoint, const ItemType & aItem, ExtraArgTypes &&... aExtraArgs)
172185
{
173-
// EncodeListItem must be called after EnsureListStarted(), thus mCurrentEncodingListIndex and
174-
// mEncodeState.mCurrentEncodingListIndex are not invalid values.
175-
if (mCurrentEncodingListIndex < mEncodeState.CurrentEncodingListIndex())
186+
if (!ShouldEncodeListItem(aCheckpoint))
176187
{
177-
// We have encoded this element in previous chunks, skip it.
178-
mCurrentEncodingListIndex++;
179188
return CHIP_NO_ERROR;
180189
}
181190

182-
TLV::TLVWriter backup;
183-
mAttributeReportIBsBuilder.Checkpoint(backup);
184-
185191
CHIP_ERROR err;
186192
if (mEncodingInitialList)
187193
{
@@ -194,19 +200,9 @@ class AttributeValueEncoder
194200
{
195201
err = EncodeAttributeReportIB(aItem, std::forward<ExtraArgTypes>(aExtraArgs)...);
196202
}
197-
if (err != CHIP_NO_ERROR)
198-
{
199-
// For list chunking, ReportEngine should not rollback the buffer when CHIP_ERROR_NO_MEMORY or similar error occurred.
200-
// However, the error might be raised in the middle of encoding procedure, then the buffer may contain partial data,
201-
// unclosed containers etc. This line clears all possible partial data and makes EncodeListItem is atomic.
202-
mAttributeReportIBsBuilder.Rollback(backup);
203-
return err;
204-
}
205203

206-
mCurrentEncodingListIndex++;
207-
mEncodeState.SetCurrentEncodingListIndex(mCurrentEncodingListIndex);
208-
mEncodedAtLeastOneListItem = true;
209-
return CHIP_NO_ERROR;
204+
PostEncodeListItem(err, aCheckpoint);
205+
return err;
210206
}
211207

212208
/**

0 commit comments

Comments
 (0)