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