Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tlv] Optimize code size of processing context tags #24224

Merged
merged 4 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1437,7 +1437,7 @@ + (id)CHIPEncodeAndDecodeNSObject:(id)object
uint8_t buffer[1024];
writer.Init(buffer, sizeof(buffer));

CHIP_ERROR error = originalData.Encode(writer, chip::TLV::Tag(1));
CHIP_ERROR error = originalData.Encode(writer, chip::TLV::CommonTag(1));
if (error != CHIP_NO_ERROR) {
MTR_LOG_ERROR("Error: Data encoding failed: %s", error.AsString());
return nil;
Expand All @@ -1456,8 +1456,9 @@ + (id)CHIPEncodeAndDecodeNSObject:(id)object
return nil;
}
__auto_type tag = reader.GetTag();
if (tag != chip::TLV::Tag(1)) {
MTR_LOG_ERROR("Error: TLV reader did not read the tag correctly: %llu", tag.mVal);
if (tag != chip::TLV::CommonTag(1)) {
MTR_LOG_ERROR("Error: TLV reader did not read the tag correctly: %x.%u", chip::TLV::ProfileIdFromTag(tag),
chip::TLV::TagNumFromTag(tag));
return nil;
}
MTRDataValueDictionaryDecodableType decodedData;
Expand Down
6 changes: 3 additions & 3 deletions src/lib/core/CHIPTLVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ CHIP_ERROR TLVReader::VerifyElement()
}
else
{
if (mElemTag == UnknownImplicitTag)
if (mElemTag == UnknownImplicitTag())
return CHIP_ERROR_UNKNOWN_IMPLICIT_TLV_TAG;
switch (mContainerType)
{
Expand Down Expand Up @@ -806,11 +806,11 @@ Tag TLVReader::ReadTag(TLVTagControl tagControl, const uint8_t *& p) const
return CommonTag(LittleEndian::Read32(p));
case TLVTagControl::ImplicitProfile_2Bytes:
if (ImplicitProfileId == kProfileIdNotSpecified)
return UnknownImplicitTag;
return UnknownImplicitTag();
return ProfileTag(ImplicitProfileId, LittleEndian::Read16(p));
case TLVTagControl::ImplicitProfile_4Bytes:
if (ImplicitProfileId == kProfileIdNotSpecified)
return UnknownImplicitTag;
return UnknownImplicitTag();
return ProfileTag(ImplicitProfileId, LittleEndian::Read32(p));
case TLVTagControl::FullyQualified_6Bytes:
vendorId = LittleEndian::Read16(p);
Expand Down
152 changes: 97 additions & 55 deletions src/lib/core/CHIPTLVTags.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,59 @@
namespace chip {
namespace TLV {

struct Tag
class Tag
{
explicit constexpr Tag(uint64_t val) : mVal(val) {}
Tag() {}
public:
enum SpecialTagNumber : uint32_t
{
kContextTagMaxNum = UINT8_MAX,
kAnonymousTagNum,
kUnknownImplicitTagNum
};

Tag() = default;

constexpr bool operator==(const Tag & other) const { return mVal == other.mVal; }
constexpr bool operator!=(const Tag & other) const { return mVal != other.mVal; }

private:
explicit constexpr Tag(uint64_t val) : mVal(val) {}

friend constexpr Tag ProfileTag(uint32_t profileId, uint32_t tagNum);
friend constexpr Tag ProfileTag(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum);
friend constexpr Tag ContextTag(uint8_t tagNum);
friend constexpr Tag CommonTag(uint32_t tagNum);
friend constexpr Tag AnonymousTag();
friend constexpr Tag UnknownImplicitTag();

// The following friend functions could be Tag class methods, but it turns out in some cases
// they may not be inlined and then passing the tag by argument/value results in smaller code
// than passing it by 'this' pointer. This can be worked around by applying 'always_inline'
// function attribute, but friend functions are likely a more portable solution.

friend constexpr uint32_t ProfileIdFromTag(Tag tag);
friend constexpr uint16_t VendorIdFromTag(Tag tag);
friend constexpr uint16_t ProfileNumFromTag(Tag tag);
friend constexpr uint32_t TagNumFromTag(Tag tag);

friend constexpr bool IsProfileTag(Tag tag);
friend constexpr bool IsContextTag(Tag tag);
friend constexpr bool IsSpecialTag(Tag tag);

static constexpr uint32_t kProfileIdShift = 32;
static constexpr uint32_t kVendorIdShift = 48;
static constexpr uint32_t kProfileNumShift = 32;
static constexpr uint32_t kSpecialTagProfileId = 0xFFFFFFFF;

// The storage of the tag value uses the following encoding:
//
// 63 47 31
// +-------------------------------+-------------------------------+----------------------------------------------+
// | Vendor id (bitwise-negated) | Profile num (bitwise-negated) | Tag number |
// +-------------------------------+-------------------------------+----------------------------------------------+
//
// Vendor id and profile number are bitwise-negated in order to optimize the code size when
// using context tags, the most commonly used tags in the SDK.
uint64_t mVal;
};

Expand All @@ -50,20 +97,6 @@ enum TLVCommonProfiles
kCommonProfileId = 0
};

// TODO: Move to private namespace
enum TLVTagFields
{
kProfileIdMask = 0xFFFFFFFF00000000ULL,
kProfileNumMask = 0x0000FFFF00000000ULL,
kVendorIdMask = 0xFFFF000000000000ULL,
kProfileIdShift = 32,
kVendorIdShift = 48,
kProfileNumShift = 32,
kTagNumMask = 0x00000000FFFFFFFFULL,
kSpecialTagMarker = 0xFFFFFFFF00000000ULL,
kContextTagMaxNum = UINT8_MAX
};

// TODO: Move to private namespace
enum class TLVTagControl : uint8_t
{
Expand Down Expand Up @@ -99,9 +132,9 @@ enum
* @param[in] tagNum The profile-specific tag number assigned to the tag.
* @return A 64-bit integer representing the tag.
*/
inline constexpr Tag ProfileTag(uint32_t profileId, uint32_t tagNum)
constexpr Tag ProfileTag(uint32_t profileId, uint32_t tagNum)
{
return Tag(((static_cast<uint64_t>(profileId)) << kProfileIdShift) | tagNum);
return Tag((static_cast<uint64_t>(~profileId) << Tag::kProfileIdShift) | tagNum);
}

/**
Expand All @@ -112,21 +145,22 @@ inline constexpr Tag ProfileTag(uint32_t profileId, uint32_t tagNum)
* @param[in] tagNum The profile-specific tag number assigned to the tag.
* @return A 64-bit integer representing the tag.
*/
inline constexpr Tag ProfileTag(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum)
constexpr Tag ProfileTag(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum)
{
return Tag(((static_cast<uint64_t>(vendorId)) << kVendorIdShift) | ((static_cast<uint64_t>(profileNum)) << kProfileNumShift) |
tagNum);
constexpr uint32_t kVendorIdShift = Tag::kVendorIdShift - Tag::kProfileIdShift;

return ProfileTag((static_cast<uint32_t>(vendorId) << kVendorIdShift) | profileNum, tagNum);
}

/**
* Generates the API representation for of context-specific TLV tag
* Generates the API representation of a context-specific TLV tag
*
* @param[in] tagNum The context-specific tag number assigned to the tag.
* @return A 64-bit integer representing the tag.
*/
inline constexpr Tag ContextTag(uint8_t tagNum)
constexpr Tag ContextTag(uint8_t tagNum)
{
return Tag(kSpecialTagMarker | tagNum);
return ProfileTag(Tag::kSpecialTagProfileId, tagNum);
}

/**
Expand All @@ -135,20 +169,26 @@ inline constexpr Tag ContextTag(uint8_t tagNum)
* @param[in] tagNum The common profile tag number assigned to the tag.
* @return A 64-bit integer representing the tag.
*/
inline constexpr Tag CommonTag(uint32_t tagNum)
constexpr Tag CommonTag(uint32_t tagNum)
{
return ProfileTag(kCommonProfileId, tagNum);
}

/**
* A value signifying a TLV element that has no tag (i.e. an anonymous element).
*/
inline constexpr Tag AnonymousTag()
constexpr Tag AnonymousTag()
{
return Tag(kSpecialTagMarker | 0x00000000FFFFFFFFULL);
return ProfileTag(Tag::kSpecialTagProfileId, Tag::kAnonymousTagNum);
}

/**
* An invalid tag that represents a TLV element decoding error due to unknown implicit profile id.
*/
constexpr Tag UnknownImplicitTag()
{
return ProfileTag(Tag::kSpecialTagProfileId, Tag::kUnknownImplicitTagNum);
}
// TODO: Move to private namespace
static constexpr Tag UnknownImplicitTag(kSpecialTagMarker | 0x00000000FFFFFFFEULL);

/**
* Returns the profile id from a TLV tag
Expand All @@ -158,9 +198,24 @@ static constexpr Tag UnknownImplicitTag(kSpecialTagMarker | 0x00000000FFFFFFFEUL
* @param[in] tag The API representation of a profile-specific TLV tag.
* @return The profile id.
*/
inline constexpr uint32_t ProfileIdFromTag(Tag tag)
constexpr uint32_t ProfileIdFromTag(Tag tag)
{
return ~static_cast<uint32_t>(tag.mVal >> Tag::kProfileIdShift);
}

/**
* Returns the vendor id from a TLV tag
*
* @note The behavior of this function is undefined if the supplied tag is not a profile-specific tag.
*
* @param[in] tag The API representation of a profile-specific TLV tag.
* @return The associated vendor id.
*/
constexpr uint16_t VendorIdFromTag(Tag tag)
{
return static_cast<uint32_t>((tag.mVal & kProfileIdMask) >> kProfileIdShift);
constexpr uint32_t kVendorIdShift = Tag::kVendorIdShift - Tag::kProfileIdShift;

return static_cast<uint16_t>(ProfileIdFromTag(tag) >> kVendorIdShift);
}

/**
Expand All @@ -171,9 +226,9 @@ inline constexpr uint32_t ProfileIdFromTag(Tag tag)
* @param[in] tag The API representation of a profile-specific TLV tag.
* @return The associated profile number.
*/
inline constexpr uint16_t ProfileNumFromTag(Tag tag)
constexpr uint16_t ProfileNumFromTag(Tag tag)
{
return static_cast<uint16_t>((tag.mVal & kProfileNumMask) >> kProfileNumShift);
return static_cast<uint16_t>(ProfileIdFromTag(tag));
}

/**
Expand All @@ -187,44 +242,31 @@ inline constexpr uint16_t ProfileNumFromTag(Tag tag)
* @param[in] tag The API representation of a profile-specific or context-specific TLV tag.
* @return The associated tag number.
*/
inline constexpr uint32_t TagNumFromTag(Tag tag)
{
return static_cast<uint32_t>(tag.mVal & kTagNumMask);
}

/**
* Returns the vendor id from a TLV tag
*
* @note The behavior of this function is undefined if the supplied tag is not a profile-specific tag.
*
* @param[in] tag The API representation of a profile-specific TLV tag.
* @return The associated vendor id.
*/
inline constexpr uint16_t VendorIdFromTag(Tag tag)
constexpr uint32_t TagNumFromTag(Tag tag)
{
return static_cast<uint16_t>((tag.mVal & kVendorIdMask) >> kVendorIdShift);
return static_cast<uint32_t>(tag.mVal);
}

/**
* Returns true of the supplied tag is a profile-specific tag.
*/
inline constexpr bool IsProfileTag(Tag tag)
constexpr bool IsProfileTag(Tag tag)
{
return (tag.mVal & kProfileIdMask) != kSpecialTagMarker;
return ProfileIdFromTag(tag) != Tag::kSpecialTagProfileId;
}

/**
* Returns true if the supplied tag is a context-specific tag.
*/
inline constexpr bool IsContextTag(Tag tag)
constexpr bool IsContextTag(Tag tag)
{
return (tag.mVal & kProfileIdMask) == kSpecialTagMarker && TagNumFromTag(tag) <= kContextTagMaxNum;
return ProfileIdFromTag(tag) == Tag::kSpecialTagProfileId && TagNumFromTag(tag) <= Tag::kContextTagMaxNum;
}

// TODO: move to private namespace
inline constexpr bool IsSpecialTag(Tag tag)
constexpr bool IsSpecialTag(Tag tag)
{
return (tag.mVal & kProfileIdMask) == kSpecialTagMarker;
return ProfileIdFromTag(tag) == Tag::kSpecialTagProfileId;
}

} // namespace TLV
Expand Down
2 changes: 1 addition & 1 deletion src/lib/core/CHIPTLVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_

if (IsSpecialTag(tag))
{
if (tagNum <= kContextTagMaxNum)
if (tagNum <= Tag::kContextTagMaxNum)
{
if (mContainerType != kTLVType_Structure && mContainerType != kTLVType_List)
return CHIP_ERROR_INVALID_TLV_TAG;
Expand Down