Skip to content

Commit a0d7b14

Browse files
Add a method for validating data-value dictionaries. (project-chip#36207)
1 parent 631ad43 commit a0d7b14

File tree

4 files changed

+413
-21
lines changed

4 files changed

+413
-21
lines changed

src/darwin/Framework/CHIP/MTRBaseDevice.mm

+35-20
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,9 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
613613
}
614614
}
615615

616-
static CHIP_ERROR MTREncodeTLVFromDataValueDictionaryInternal(id object, chip::TLV::TLVWriter & writer, chip::TLV::Tag tag)
616+
// writer is allowed to be null to just validate the incoming object without
617+
// actually encoding.
618+
static CHIP_ERROR MTREncodeTLVFromDataValueDictionaryInternal(id object, chip::TLV::TLVWriter * writer, chip::TLV::Tag tag)
617619
{
618620
if (![object isKindOfClass:[NSDictionary class]]) {
619621
MTR_LOG_ERROR("Error: Unsupported object to encode: %@", [object class]);
@@ -631,60 +633,62 @@ static CHIP_ERROR MTREncodeTLVFromDataValueDictionaryInternal(id object, chip::T
631633
MTR_LOG_ERROR("Error: Object to encode has corrupt signed integer type: %@", [value class]);
632634
return CHIP_ERROR_INVALID_ARGUMENT;
633635
}
634-
return writer.Put(tag, [value longLongValue]);
636+
return writer ? writer->Put(tag, [value longLongValue]) : CHIP_NO_ERROR;
635637
}
636638
if ([typeName isEqualToString:MTRUnsignedIntegerValueType]) {
637639
if (![value isKindOfClass:[NSNumber class]]) {
638640
MTR_LOG_ERROR("Error: Object to encode has corrupt unsigned integer type: %@", [value class]);
639641
return CHIP_ERROR_INVALID_ARGUMENT;
640642
}
641-
return writer.Put(tag, [value unsignedLongLongValue]);
643+
return writer ? writer->Put(tag, [value unsignedLongLongValue]) : CHIP_NO_ERROR;
642644
}
643645
if ([typeName isEqualToString:MTRBooleanValueType]) {
644646
if (![value isKindOfClass:[NSNumber class]]) {
645647
MTR_LOG_ERROR("Error: Object to encode has corrupt boolean type: %@", [value class]);
646648
return CHIP_ERROR_INVALID_ARGUMENT;
647649
}
648-
return writer.Put(tag, static_cast<bool>([value boolValue]));
650+
return writer ? writer->Put(tag, static_cast<bool>([value boolValue])) : CHIP_NO_ERROR;
649651
}
650652
if ([typeName isEqualToString:MTRFloatValueType]) {
651653
if (![value isKindOfClass:[NSNumber class]]) {
652654
MTR_LOG_ERROR("Error: Object to encode has corrupt float type: %@", [value class]);
653655
return CHIP_ERROR_INVALID_ARGUMENT;
654656
}
655-
return writer.Put(tag, [value floatValue]);
657+
return writer ? writer->Put(tag, [value floatValue]) : CHIP_NO_ERROR;
656658
}
657659
if ([typeName isEqualToString:MTRDoubleValueType]) {
658660
if (![value isKindOfClass:[NSNumber class]]) {
659661
MTR_LOG_ERROR("Error: Object to encode has corrupt double type: %@", [value class]);
660662
return CHIP_ERROR_INVALID_ARGUMENT;
661663
}
662-
return writer.Put(tag, [value doubleValue]);
664+
return writer ? writer->Put(tag, [value doubleValue]) : CHIP_NO_ERROR;
663665
}
664666
if ([typeName isEqualToString:MTRNullValueType]) {
665-
return writer.PutNull(tag);
667+
return writer ? writer->PutNull(tag) : CHIP_NO_ERROR;
666668
}
667669
if ([typeName isEqualToString:MTRUTF8StringValueType]) {
668670
if (![value isKindOfClass:[NSString class]]) {
669671
MTR_LOG_ERROR("Error: Object to encode has corrupt UTF8 string type: %@", [value class]);
670672
return CHIP_ERROR_INVALID_ARGUMENT;
671673
}
672-
return writer.PutString(tag, AsCharSpan(value));
674+
return writer ? writer->PutString(tag, AsCharSpan(value)) : CHIP_NO_ERROR;
673675
}
674676
if ([typeName isEqualToString:MTROctetStringValueType]) {
675677
if (![value isKindOfClass:[NSData class]]) {
676678
MTR_LOG_ERROR("Error: Object to encode has corrupt octet string type: %@", [value class]);
677679
return CHIP_ERROR_INVALID_ARGUMENT;
678680
}
679-
return writer.Put(tag, AsByteSpan(value));
681+
return writer ? writer->Put(tag, AsByteSpan(value)) : CHIP_NO_ERROR;
680682
}
681683
if ([typeName isEqualToString:MTRStructureValueType]) {
682684
if (![value isKindOfClass:[NSArray class]]) {
683685
MTR_LOG_ERROR("Error: Object to encode has corrupt structure type: %@", [value class]);
684686
return CHIP_ERROR_INVALID_ARGUMENT;
685687
}
686-
TLV::TLVType outer;
687-
ReturnErrorOnFailure(writer.StartContainer(tag, chip::TLV::kTLVType_Structure, outer));
688+
TLV::TLVType outer = TLV::kTLVType_NotSpecified;
689+
if (writer) {
690+
ReturnErrorOnFailure(writer->StartContainer(tag, chip::TLV::kTLVType_Structure, outer));
691+
}
688692
for (id element in value) {
689693
if (![element isKindOfClass:[NSDictionary class]]) {
690694
MTR_LOG_ERROR("Error: Structure element to encode has corrupt type: %@", [element class]);
@@ -713,16 +717,20 @@ static CHIP_ERROR MTREncodeTLVFromDataValueDictionaryInternal(id object, chip::T
713717
ReturnErrorOnFailure(
714718
MTREncodeTLVFromDataValueDictionaryInternal(elementValue, writer, tag));
715719
}
716-
ReturnErrorOnFailure(writer.EndContainer(outer));
720+
if (writer) {
721+
ReturnErrorOnFailure(writer->EndContainer(outer));
722+
}
717723
return CHIP_NO_ERROR;
718724
}
719725
if ([typeName isEqualToString:MTRArrayValueType]) {
720726
if (![value isKindOfClass:[NSArray class]]) {
721727
MTR_LOG_ERROR("Error: Object to encode has corrupt array type: %@", [value class]);
722728
return CHIP_ERROR_INVALID_ARGUMENT;
723729
}
724-
TLV::TLVType outer;
725-
ReturnErrorOnFailure(writer.StartContainer(tag, chip::TLV::kTLVType_Array, outer));
730+
TLV::TLVType outer = TLV::kTLVType_NotSpecified;
731+
if (writer) {
732+
ReturnErrorOnFailure(writer->StartContainer(tag, chip::TLV::kTLVType_Array, outer));
733+
}
726734
for (id element in value) {
727735
if (![element isKindOfClass:[NSDictionary class]]) {
728736
MTR_LOG_ERROR("Error: Array element to encode has corrupt type: %@", [element class]);
@@ -735,14 +743,16 @@ static CHIP_ERROR MTREncodeTLVFromDataValueDictionaryInternal(id object, chip::T
735743
}
736744
ReturnErrorOnFailure(MTREncodeTLVFromDataValueDictionaryInternal(elementValue, writer, chip::TLV::AnonymousTag()));
737745
}
738-
ReturnErrorOnFailure(writer.EndContainer(outer));
746+
if (writer) {
747+
ReturnErrorOnFailure(writer->EndContainer(outer));
748+
}
739749
return CHIP_NO_ERROR;
740750
}
741751
MTR_LOG_ERROR("Error: Unsupported type to encode: %@", typeName);
742752
return CHIP_ERROR_INVALID_ARGUMENT;
743753
}
744754

745-
static CHIP_ERROR MTREncodeTLVFromDataValueDictionary(id object, chip::TLV::TLVWriter & writer, chip::TLV::Tag tag)
755+
static CHIP_ERROR MTREncodeTLVFromDataValueDictionary(id object, chip::TLV::TLVWriter * writer, chip::TLV::Tag tag)
746756
{
747757
CHIP_ERROR err = MTREncodeTLVFromDataValueDictionaryInternal(object, writer, tag);
748758
if (err != CHIP_NO_ERROR) {
@@ -761,7 +771,7 @@ static CHIP_ERROR MTREncodeTLVFromDataValueDictionary(id object, chip::TLV::TLVW
761771
TLV::TLVWriter writer;
762772
writer.Init(buffer);
763773

764-
CHIP_ERROR err = MTREncodeTLVFromDataValueDictionary(value, writer, TLV::AnonymousTag());
774+
CHIP_ERROR err = MTREncodeTLVFromDataValueDictionary(value, &writer, TLV::AnonymousTag());
765775
if (err != CHIP_NO_ERROR) {
766776
if (error) {
767777
*error = [MTRError errorForCHIPErrorCode:err];
@@ -772,6 +782,11 @@ static CHIP_ERROR MTREncodeTLVFromDataValueDictionary(id object, chip::TLV::TLVW
772782
return AsData(ByteSpan(buffer, writer.GetLengthWritten()));
773783
}
774784

785+
BOOL MTRDataValueDictionaryIsWellFormed(MTRDeviceDataValueDictionary value)
786+
{
787+
return MTREncodeTLVFromDataValueDictionary(value, nullptr, TLV::AnonymousTag()) == CHIP_NO_ERROR;
788+
}
789+
775790
// Callback type to pass data value as an NSObject
776791
typedef void (*MTRDataValueDictionaryCallback)(void * context, id value);
777792

@@ -798,7 +813,7 @@ CHIP_ERROR Decode(chip::TLV::TLVReader & data)
798813

799814
CHIP_ERROR Encode(chip::TLV::TLVWriter & writer, chip::TLV::Tag tag) const
800815
{
801-
return MTREncodeTLVFromDataValueDictionary(decodedObj, writer, tag);
816+
return MTREncodeTLVFromDataValueDictionary(decodedObj, &writer, tag);
802817
}
803818

804819
static constexpr bool kIsFabricScoped = false;
@@ -2212,7 +2227,7 @@ + (NSDictionary *)eventReportForHeader:(const chip::app::EventHeader &)header an
22122227
// Commands never need chained buffers, since they cannot be chunked.
22132228
writer.Init(std::move(buffer), /* useChainedBuffers = */ false);
22142229

2215-
CHIP_ERROR errorCode = MTREncodeTLVFromDataValueDictionary(data, writer, TLV::AnonymousTag());
2230+
CHIP_ERROR errorCode = MTREncodeTLVFromDataValueDictionary(data, &writer, TLV::AnonymousTag());
22162231
if (errorCode != CHIP_NO_ERROR) {
22172232
LogStringAndReturnError(@"Unable to encode data-value to TLV", errorCode, error);
22182233
return System::PacketBufferHandle();
@@ -3082,7 +3097,7 @@ static bool EncodeDataValueToTLV(System::PacketBufferHandle & buffer, Platform::
30823097
System::PacketBufferTLVWriter writer;
30833098
writer.Init(std::move(buffer), /* useChainedBuffers = */ true);
30843099

3085-
CHIP_ERROR errorCode = MTREncodeTLVFromDataValueDictionary(data, writer, TLV::AnonymousTag());
3100+
CHIP_ERROR errorCode = MTREncodeTLVFromDataValueDictionary(data, &writer, TLV::AnonymousTag());
30863101
if (errorCode != CHIP_NO_ERROR) {
30873102
LogStringAndReturnError(@"Unable to encode data-value to TLV", errorCode, error);
30883103
return false;

src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#import "MTRBaseDevice.h"
1919
#import <Foundation/Foundation.h>
2020

21+
#import "MTRDeviceDataValueDictionary.h"
22+
2123
#include <app/AttributePathParams.h>
2224
#include <app/ConcreteAttributePath.h>
2325
#include <app/ConcreteCommandPath.h>
@@ -257,6 +259,6 @@ NSDictionary<NSString *, id> * _Nullable MTRDecodeDataValueDictionaryFromCHIPTLV
257259
// TLV Data with an anonymous tag. This method assumes the encoding of the
258260
// value fits in a single UDP MTU; for lists this method might need to be used
259261
// on each list item separately.
260-
NSData * _Nullable MTREncodeTLVFromDataValueDictionary(NSDictionary<NSString *, id> * value, NSError * __autoreleasing * error);
262+
NSData * _Nullable MTREncodeTLVFromDataValueDictionary(MTRDeviceDataValueDictionary value, NSError * __autoreleasing * error);
261263

262264
NS_ASSUME_NONNULL_END

src/darwin/Framework/CHIP/MTRDevice_Internal.h

+7
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ MTR_DIRECT_MEMBERS
176176
- (void)devicePrivateInternalStateChanged:(MTRDevice *)device internalState:(NSDictionary *)state;
177177
@end
178178

179+
// Returns whether a data-value dictionary is well-formed (in the sense that all
180+
// the types of the objects inside are as expected, so it's actually a valid
181+
// representation of some TLV). Implemented in MTRBaseDevice.mm because that's
182+
// where the pieces needed to implement it are, but declared here so our tests
183+
// can see it.
184+
MTR_EXTERN MTR_TESTABLE BOOL MTRDataValueDictionaryIsWellFormed(MTRDeviceDataValueDictionary value);
185+
179186
#pragma mark - Constants
180187

181188
static NSString * const kDefaultSubscriptionPoolSizeOverrideKey = @"subscriptionPoolSizeOverride";

0 commit comments

Comments
 (0)