Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
erjiaqing committed Nov 22, 2021
1 parent cee770f commit 2533673
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 70 deletions.
69 changes: 69 additions & 0 deletions src/app/AttributeAccessInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <app/AttributeAccessInterface.h>

namespace chip {
namespace app {
CHIP_ERROR AttributeValueEncoder::EncodeAttributePathIB(AttributePathIB::Builder & aAttributePathIBBuilder)
{
aAttributePathIBBuilder.Endpoint(mPath.mEndpointId).Cluster(mPath.mClusterId).Attribute(mPath.mAttributeId);

// Encode the list index field if we are encoding a single element in an list.
if (mCurrentEncodingListIndex != kInvalidListIndex)
{
aAttributePathIBBuilder.ListIndex(mCurrentEncodingListIndex);
}

return aAttributePathIBBuilder.EndOfAttributePathIB().GetError();
}

CHIP_ERROR AttributeValueEncoder::EncodeEmptyList()
{
if (mCurrentEncodingListIndex == kInvalidListIndex)
{
if (mEncodeState.mCurrentEncodingListIndex == kInvalidListIndex)
{
// This part is not atomic, the
mEncodeState.mAllowPartialData = false;
// Spec 10.5.4.3.1, 10.5.4.6 (Replace a list w/ Multiple IBs)
// Put a empty array before encoding the first array element for list chunking.
AttributeReportIB::Builder attributeReportIBBuilder = mAttributeReportIBsBuilder.CreateAttributeReport();
AttributeDataIB::Builder attributeDataIBBuilder = attributeReportIBBuilder.CreateAttributeData();

// mCurrentEncodingListIndex is an invalid list index now, thus EncodeAttributePathIB won't encode it in the
// AttributePathIB.
ReturnErrorOnFailure(EncodeAttributePathIB(attributeDataIBBuilder.CreatePath()));

ReturnErrorOnFailure(
TagBoundEncoder(attributeDataIBBuilder.GetWriter(), TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)))
.EncodeList([](const TagBoundEncoder &) -> CHIP_ERROR { return CHIP_NO_ERROR; }));

attributeDataIBBuilder.DataVersion(mDataVersion).EndOfAttributeDataIB();
ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
ReturnErrorOnFailure(attributeReportIBBuilder.EndOfAttributeReportIB().GetError());
mEncodeState.mCurrentEncodingListIndex = 0;
}
mCurrentEncodingListIndex = 0;
}

return CHIP_NO_ERROR;
}

} // namespace app
} // namespace chip
65 changes: 20 additions & 45 deletions src/app/AttributeAccessInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ namespace chip {
namespace app {

/**
* The AttributeVValueEncoder is a helper class for filling report payloads into AttributeReportIBs.
* The AttributeValueEncoder is a helper class for filling report payloads into AttributeReportIBs.
* The attribute value encoder can be initialized with a AttributeEncodeState for saving and recovering its state between encode
* sessions (chunkings).
*
* When Encode returned recoverable errors (e.g. CHIP_ERROR_NO_MEMORY) the state can be used to initialize the AttributeValueEncoder
* When Encode returns recoverable errors (e.g. CHIP_ERROR_NO_MEMORY) the state can be used to initialize the AttributeValueEncoder
* for future use on the same attribute path.
*/
class AttributeValueEncoder
Expand Down Expand Up @@ -96,19 +96,11 @@ class AttributeValueEncoder
AttributeReportIB::Builder attributeReportIBBuilder = mAttributeReportIBsBuilder.CreateAttributeReport();
AttributeDataIB::Builder attributeDataIBBuilder = attributeReportIBBuilder.CreateAttributeData();
AttributePathIB::Builder attributePathIBBuilder = attributeDataIBBuilder.CreatePath();
attributePathIBBuilder.Endpoint(mPath.mEndpointId).Cluster(mPath.mClusterId).Attribute(mPath.mAttributeId);

// Encode the list index field if we are encoding a single element in an list.
if (mCurrentEncodingListIndex != kInvalidListIndex)
{
attributePathIBBuilder.ListIndex(mCurrentEncodingListIndex);
}

ReturnErrorOnFailure(attributePathIBBuilder.EndOfAttributePathIB().GetError());

ReturnErrorOnFailure(
TagBoundEncoder(attributeDataIBBuilder.GetWriter(), TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)))
.Encode(std::forward<Ts>(aArgs)...));
ReturnErrorOnFailure(EncodeAttributePathIB(attributePathIBBuilder));
ReturnErrorOnFailure(DataModel::Encode(*attributeDataIBBuilder.GetWriter(),
TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)),
std::forward<Ts>(aArgs)...));

attributeDataIBBuilder.DataVersion(mDataVersion);

Expand All @@ -119,35 +111,9 @@ class AttributeValueEncoder
template <typename... Ts>
CHIP_ERROR EncodeListItem(Ts... aArgs)
{
if (mCurrentEncodingListIndex == kInvalidListIndex)
{
if (mEncodeState.mCurrentEncodingListIndex == kInvalidListIndex)
{
// Spec 10.5.4.3.1, 10.5.4.6 (Replace a list w/ Multiple IBs)
// Put a empty array before encoding the first array element for list chunking.
AttributeReportIB::Builder attributeReportIBBuilder = mAttributeReportIBsBuilder.CreateAttributeReport();
AttributeDataIB::Builder attributeDataIBBuilder = attributeReportIBBuilder.CreateAttributeData();
AttributePathIB::Builder attributePathIBBuilder = attributeDataIBBuilder.CreatePath();

ReturnErrorOnFailure(attributePathIBBuilder.Endpoint(mPath.mEndpointId)
.Cluster(mPath.mClusterId)
.Attribute(mPath.mAttributeId)
.EndOfAttributePathIB()
.GetError());

ReturnErrorOnFailure(
TagBoundEncoder(attributeDataIBBuilder.GetWriter(), TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)))
.EncodeList([](const TagBoundEncoder &) -> CHIP_ERROR { return CHIP_NO_ERROR; }));

attributeDataIBBuilder.DataVersion(mDataVersion).EndOfAttributeDataIB();
ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
ReturnErrorOnFailure(attributeReportIBBuilder.EndOfAttributeReportIB().GetError());
mEncodeState.mCurrentEncodingListIndex = 0;
}
mCurrentEncodingListIndex = 0;
}
ReturnErrorOnFailure(EncodeEmptyList());

// We make Encode atomic here, so need to tell the caller do not rollback when we met any error during encoding.
// We make Encode atomic here, so need to tell the caller to not rollback when we encounter an error during encoding.
mEncodeState.mAllowPartialData = true;

if (mCurrentEncodingListIndex < mEncodeState.mCurrentEncodingListIndex)
Expand All @@ -163,7 +129,9 @@ class AttributeValueEncoder
CHIP_ERROR err = Encode(std::forward<Ts>(aArgs)...);
if (err != CHIP_NO_ERROR)
{
// Rollback on error, then EncodeListItem is atomic.
// For list chunking, ReportEngine should not rollback the buffer when CHIP_NO_MEMORY or similar error occurred.
// However, the error might be raised in the middle of encoding procedure, then the buffer may contain partial data,
// unclosed containers etc. This line clears all possible partial data and makes EncodeListItem is atomic.
mAttributeReportIBsBuilder.Rollback(backup);
return err;
}
Expand Down Expand Up @@ -202,9 +170,16 @@ class AttributeValueEncoder

AttributeEncodeState GetState() { return mEncodeState; }

void FinishManualEncode() {}

private:
/**
* EncodeListReportHeader encodes the first item of one report with lists (an empty list).
*
* If internal state indicates we have already encoded the empty list, this function will do nothing and return CHIP_NO_ERROR.
*/
CHIP_ERROR EncodeEmptyList();

CHIP_ERROR EncodeAttributePathIB(AttributePathIB::Builder &);

bool mTriedEncode = false;
AttributeReportIBs::Builder & mAttributeReportIBsBuilder;
const FabricIndex mAccessingFabricIndex;
Expand Down
1 change: 1 addition & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ static_library("app") {
output_name = "libCHIPDataModel"

sources = [
"AttributeAccessInterface.cpp",
"AttributePathExpandIterator.cpp",
"AttributePathExpandIterator.h",
"AttributePathParams.cpp",
Expand Down
23 changes: 11 additions & 12 deletions src/app/clusters/descriptor/descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ CHIP_ERROR DescriptorAttrAccess::ReadPartsAttribute(EndpointId endpoint, Attribu

if (endpoint == 0x00)
{
err = aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
err = aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (uint16_t index = 0; index < emberAfEndpointCount(); index++)
{
if (emberAfEndpointIndexIsEnabled(index))
Expand All @@ -85,7 +85,7 @@ CHIP_ERROR DescriptorAttrAccess::ReadPartsAttribute(EndpointId endpoint, Attribu

CHIP_ERROR DescriptorAttrAccess::ReadDeviceAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder)
{
CHIP_ERROR err = aEncoder.EncodeList([&endpoint](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
CHIP_ERROR err = aEncoder.EncodeList([&endpoint](auto encoder) -> CHIP_ERROR {
Descriptor::Structs::DeviceType::Type deviceStruct;
uint16_t index = emberAfIndexFromEndpoint(endpoint);

Expand All @@ -99,18 +99,17 @@ CHIP_ERROR DescriptorAttrAccess::ReadDeviceAttribute(EndpointId endpoint, Attrib

CHIP_ERROR DescriptorAttrAccess::ReadClientServerAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder, bool server)
{
CHIP_ERROR err =
aEncoder.EncodeList([&endpoint, server](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
uint16_t clusterCount = emberAfClusterCount(endpoint, server);
CHIP_ERROR err = aEncoder.EncodeList([&endpoint, server](auto encoder) -> CHIP_ERROR {
uint16_t clusterCount = emberAfClusterCount(endpoint, server);

for (uint8_t clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++)
{
EmberAfCluster * cluster = emberAfGetNthCluster(endpoint, clusterIndex, server);
ReturnErrorOnFailure(encoder.Encode(cluster->clusterId));
}
for (uint8_t clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++)
{
EmberAfCluster * cluster = emberAfGetNthCluster(endpoint, clusterIndex, server);
ReturnErrorOnFailure(encoder.Encode(cluster->clusterId));
}

return CHIP_NO_ERROR;
});
return CHIP_NO_ERROR;
});

return err;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ CHIP_ERROR GeneralDiagosticsAttrAccess::ReadListIfSupported(CHIP_ERROR (Platform

if ((PlatformMgr().*getter)(faultList) == CHIP_NO_ERROR)
{
err = aEncoder.EncodeList([&faultList](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
err = aEncoder.EncodeList([&faultList](auto encoder) -> CHIP_ERROR {
for (auto fault : faultList)
{
ReturnErrorOnFailure(encoder.Encode(fault));
Expand All @@ -105,7 +105,7 @@ CHIP_ERROR GeneralDiagosticsAttrAccess::ReadNetworkInterfaces(AttributeValueEnco

if (ConnectivityMgr().GetNetworkInterfaces(&netifs) == CHIP_NO_ERROR)
{
err = aEncoder.EncodeList([&netifs](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
err = aEncoder.EncodeList([&netifs](auto encoder) -> CHIP_ERROR {
for (DeviceLayer::NetworkInterface * ifp = netifs; ifp != nullptr; ifp = ifp->Next)
{
ReturnErrorOnFailure(encoder.Encode(*ifp));
Expand Down
2 changes: 1 addition & 1 deletion src/app/clusters/mode-select-server/mode-select-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ CHIP_ERROR ModeSelectAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
return CHIP_NO_ERROR;
}
CHIP_ERROR err;
err = aEncoder.EncodeList([modeOptionsProvider](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
err = aEncoder.EncodeList([modeOptionsProvider](auto encoder) -> CHIP_ERROR {
const auto * end = modeOptionsProvider.end();
for (auto * it = modeOptionsProvider.begin(); it != end; ++it)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ CHIP_ERROR OperationalCredentialsAttrAccess::ReadCommissionedFabrics(EndpointId

CHIP_ERROR OperationalCredentialsAttrAccess::ReadFabricsList(EndpointId endpoint, AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
return aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (auto & fabricInfo : Server::GetInstance().GetFabricTable())
{
if (!fabricInfo.IsInitialized())
Expand All @@ -114,7 +114,7 @@ CHIP_ERROR OperationalCredentialsAttrAccess::ReadFabricsList(EndpointId endpoint

CHIP_ERROR OperationalCredentialsAttrAccess::ReadRootCertificates(EndpointId endpoint, AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
return aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (auto & fabricInfo : Server::GetInstance().GetFabricTable())
{
ByteSpan cert;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ CHIP_ERROR SoftwareDiagosticsAttrAccess::ReadThreadMetrics(AttributeValueEncoder

if (DeviceLayer::PlatformMgr().GetThreadMetrics(&threadMetrics) == CHIP_NO_ERROR)
{
err = aEncoder.EncodeList([&threadMetrics](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
err = aEncoder.EncodeList([&threadMetrics](auto encoder) -> CHIP_ERROR {
for (DeviceLayer::ThreadMetrics * thread = threadMetrics; thread != nullptr; thread = thread->Next)
{
ReturnErrorOnFailure(encoder.Encode(*thread));
Expand Down
8 changes: 4 additions & 4 deletions src/app/clusters/test-cluster-server/test-cluster-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ CHIP_ERROR TestAttrAccess::Write(const ConcreteDataAttributePath & aPath, Attrib

CHIP_ERROR TestAttrAccess::ReadListInt8uAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
return aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
ReturnErrorOnFailure(encoder.Encode(gListUint8Data[index]));
Expand Down Expand Up @@ -166,7 +166,7 @@ CHIP_ERROR TestAttrAccess::WriteListInt8uAttribute(AttributeValueDecoder & aDeco

CHIP_ERROR TestAttrAccess::ReadListOctetStringAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
return aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
ByteSpan span(gListOctetStringData[index].Data(), gListOctetStringData[index].Length());
Expand Down Expand Up @@ -200,7 +200,7 @@ CHIP_ERROR TestAttrAccess::WriteListOctetStringAttribute(AttributeValueDecoder &

CHIP_ERROR TestAttrAccess::ReadListStructOctetStringAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
return aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
Structs::TestListStructOctet::Type structOctet;
Expand Down Expand Up @@ -246,7 +246,7 @@ CHIP_ERROR TestAttrAccess::WriteListStructOctetStringAttribute(AttributeValueDec

CHIP_ERROR TestAttrAccess::ReadListNullablesAndOptionalsStructAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
return aEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
// Just encode a single default-initialized
// entry for now.
Structs::NullablesAndOptionalsStruct::Type entry;
Expand Down
4 changes: 2 additions & 2 deletions src/app/tests/TestAttributeValueEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void TestEncodeListOfBools2(nlTestSuite * aSuite, void * aContext)
{
TestSetup test(aSuite);
bool list[] = { true, false };
CHIP_ERROR err = test.encoder.EncodeList([&list](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
CHIP_ERROR err = test.encoder.EncodeList([&list](auto encoder) -> CHIP_ERROR {
for (auto & item : list)
{
ReturnErrorOnFailure(encoder.Encode(item));
Expand Down Expand Up @@ -217,7 +217,7 @@ void TestEncodeListChunking(nlTestSuite * aSuite, void * aContext)
AttributeValueEncoder::AttributeEncodeState state;

bool list[] = { true, false };
auto listEncoder = [&list](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
auto listEncoder = [&list](auto encoder) -> CHIP_ERROR {
for (auto & item : list)
{
ReturnErrorOnFailure(encoder.Encode(item));
Expand Down
2 changes: 1 addition & 1 deletion src/app/util/mock/attribute-storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const Co
(apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState);
AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, 0, state);

CHIP_ERROR err = valueEncoder.EncodeList([](const AttributeValueEncoder::ListEncodeHelper & encoder) -> CHIP_ERROR {
CHIP_ERROR err = valueEncoder.EncodeList([](auto encoder) -> CHIP_ERROR {
for (int i = 0; i < 6; i++)
{
ReturnErrorOnFailure(encoder.Encode(chip::ByteSpan(mockAttribute4, sizeof(mockAttribute4))));
Expand Down

0 comments on commit 2533673

Please sign in to comment.