Skip to content

Commit

Permalink
Improve DecodableList when not in list (project-chip#13607)
Browse files Browse the repository at this point in the history
* Add support for decoding null valued lists

Spec treats empty lists and null valued lists equivalently.

However DecodableList expects a container, even if empty, and does
not support the case where there is no container (i.e. null valued
list).

Add support for null valued list (i.e. no container) by handling
it as a special case in the Decode function, the DecodableList,
and its Iterator. This is done using an "empty" mReader (with
container type "not specified") in the "no list" case.

* Remove changes to Decode

Keep the other changes, and adjust the documentation.
  • Loading branch information
mlepage-google authored and step0035 committed Feb 8, 2022
1 parent 96d040b commit 4ac1214
Showing 1 changed file with 34 additions and 18 deletions.
52 changes: 34 additions & 18 deletions src/app/data-model/DecodableList.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,40 @@ namespace DataModel {
* This class provides an iteratable decoder of list items within TLV payloads
* such that no memory has to be provided ahead of time to store the entirety of the decoded
* list contents.
*
*/
template <typename T>
class DecodableList
{
public:
DecodableList()
{
// Init to an empty list.
mReader.Init(nullptr, 0);
}
DecodableList() { ClearReader(); }

/*
* @brief
*
* This call stores a TLV reader positioned on the list this class is to manage.
*
* Specifically, the passed-in TLV reader should be pointing into the list just after
* Specifically, the passed-in reader should be pointing into the list just after
* having called `OpenContainer` on the list element.
*/
void SetReader(const TLV::TLVReader & reader) { mReader = reader; }

/*
* @brief
*
* This call clears the TLV reader managed by this class, so it can be reused.
*/
void ClearReader() { mReader.Init(nullptr, 0); }

class Iterator
{
public:
/*
* Initialize the iterator with a reference to a reader.
*
* This reader should be pointing into the list just after
* having called `OpenContainer` on the list element.
* having called `OpenContainer` on the list element, or should
* have a `kTLVType_NotSpecified` container type if there is
* no list.
*/
Iterator(const TLV::TLVReader & reader)
{
Expand All @@ -77,16 +81,20 @@ class DecodableList
* If an element does exist and was successfully decoded, this
* shall return true.
*
* Otherwise, if the end of list is reached, this call shall return
* false.
* Otherwise, if the end of list is reached, or there was no list,
* this call shall return false.
*
* If an error was encountered at any point during the iteration or decode,
* this shall return false as well. The caller is expected to invoke GetStatus()
* to retrieve the status of the operation.
*
*/
bool Next()
{
if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
{
return false;
}

if (mStatus == CHIP_NO_ERROR)
{
mStatus = mReader.Next();
Expand All @@ -111,7 +119,6 @@ class DecodableList
*
* Notably, if the end-of-list was encountered in a previous call to Next,
* the status returned shall be CHIP_NO_ERROR.
*
*/
CHIP_ERROR GetStatus() const
{
Expand All @@ -134,12 +141,24 @@ class DecodableList
Iterator begin() const { return Iterator(mReader); }

/*
* Compute the size of the list. This can fail if the TLV is malformed. If
* Compute the size of the list. This can fail if the TLV is malformed. If
* this succeeds, that does not guarantee that the individual items can be
* successfully decoded; consumers should check Iterator::GetStatus() when
* actually decoding them.
* actually decoding them. If there is no list then the size is considered
* to be zero.
*/
CHIP_ERROR ComputeSize(size_t * size) const { return mReader.CountRemainingInContainer(size); }
CHIP_ERROR ComputeSize(size_t * size) const
{
if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
{
*size = 0;
return CHIP_NO_ERROR;
}
else
{
return mReader.CountRemainingInContainer(size);
}
}

private:
TLV::TLVReader mReader;
Expand All @@ -149,13 +168,10 @@ template <typename X>
CHIP_ERROR Decode(TLV::TLVReader & reader, DecodableList<X> & x)
{
VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH);

TLV::TLVType type;

ReturnErrorOnFailure(reader.EnterContainer(type));
x.SetReader(reader);
ReturnErrorOnFailure(reader.ExitContainer(type));

return CHIP_NO_ERROR;
}

Expand Down

0 comments on commit 4ac1214

Please sign in to comment.