@@ -55,17 +55,22 @@ class AttributeValueEncoder
55
55
VerifyOrReturnError (!mAttributeValueEncoder .mIsFabricFiltered ||
56
56
aArg.GetFabricIndex () == mAttributeValueEncoder .AccessingFabricIndex (),
57
57
CHIP_NO_ERROR);
58
- return mAttributeValueEncoder .EncodeListItem (aArg, mAttributeValueEncoder .AccessingFabricIndex ());
58
+ return mAttributeValueEncoder .EncodeListItem (mCheckpoint , aArg, mAttributeValueEncoder .AccessingFabricIndex ());
59
59
}
60
60
61
61
template <typename T, std::enable_if_t <!DataModel::IsFabricScoped<T>::value, bool > = true >
62
62
CHIP_ERROR Encode (const T & aArg) const
63
63
{
64
- return mAttributeValueEncoder .EncodeListItem (aArg);
64
+ return mAttributeValueEncoder .EncodeListItem (mCheckpoint , aArg);
65
65
}
66
66
67
67
private:
68
68
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 ;
69
74
};
70
75
71
76
AttributeValueEncoder (AttributeReportIBs::Builder & aAttributeReportIBsBuilder, Access::SubjectDescriptor subjectDescriptor,
@@ -163,25 +168,26 @@ class AttributeValueEncoder
163
168
friend class ListEncodeHelper ;
164
169
friend class TestOnlyAttributeValueEncoderAccessor ;
165
170
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
+
166
179
// EncodeListItem may be given an extra FabricIndex argument as a second
167
180
// arg, or not. Represent that via a parameter pack (which might be
168
181
// empty). In practice, for any given ItemType the extra arg is either there
169
182
// or not, so we don't get more template explosion due to aExtraArgs.
170
183
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)
172
185
{
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))
176
187
{
177
- // We have encoded this element in previous chunks, skip it.
178
- mCurrentEncodingListIndex ++;
179
188
return CHIP_NO_ERROR;
180
189
}
181
190
182
- TLV::TLVWriter backup;
183
- mAttributeReportIBsBuilder .Checkpoint (backup);
184
-
185
191
CHIP_ERROR err;
186
192
if (mEncodingInitialList )
187
193
{
@@ -194,19 +200,9 @@ class AttributeValueEncoder
194
200
{
195
201
err = EncodeAttributeReportIB (aItem, std::forward<ExtraArgTypes>(aExtraArgs)...);
196
202
}
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
- }
205
203
206
- mCurrentEncodingListIndex ++;
207
- mEncodeState .SetCurrentEncodingListIndex (mCurrentEncodingListIndex );
208
- mEncodedAtLeastOneListItem = true ;
209
- return CHIP_NO_ERROR;
204
+ PostEncodeListItem (err, aCheckpoint);
205
+ return err;
210
206
}
211
207
212
208
/* *
0 commit comments