Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * Copyright (c) 2013-2017 Nest Labs, Inc.
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 : #pragma once
19 :
20 : #include <lib/support/StringBuilder.h>
21 : #include <lib/support/TypeTraits.h>
22 :
23 : #include <cstdint>
24 : #include <type_traits>
25 :
26 : namespace chip {
27 : namespace TLV {
28 :
29 : class Tag
30 : {
31 : public:
32 : enum SpecialTagNumber : uint32_t
33 : {
34 : kContextTagMaxNum = UINT8_MAX,
35 : kAnonymousTagNum,
36 : kUnknownImplicitTagNum
37 : };
38 :
39 : Tag() = default;
40 :
41 5288519 : constexpr bool operator==(const Tag & other) const { return mVal == other.mVal; }
42 1300599 : constexpr bool operator!=(const Tag & other) const { return mVal != other.mVal; }
43 :
44 : /// Appends the text representation of the tag to the given string builder base.
45 : StringBuilderBase & AppendTo(StringBuilderBase & out);
46 :
47 : private:
48 10378318 : explicit constexpr Tag(uint64_t val) : mVal(val) {}
49 :
50 : friend constexpr Tag ProfileTag(uint32_t profileId, uint32_t tagNum);
51 : friend constexpr Tag ProfileTag(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum);
52 : friend constexpr Tag ContextTag(uint8_t tagNum);
53 : friend constexpr Tag CommonTag(uint32_t tagNum);
54 : friend constexpr Tag AnonymousTag();
55 : friend constexpr Tag UnknownImplicitTag();
56 :
57 : // The following friend functions could be Tag class methods, but it turns out in some cases
58 : // they may not be inlined and then passing the tag by argument/value results in smaller code
59 : // than passing it by 'this' pointer. This can be worked around by applying 'always_inline'
60 : // function attribute, but friend functions are likely a more portable solution.
61 :
62 : friend constexpr uint32_t ProfileIdFromTag(Tag tag);
63 : friend constexpr uint16_t VendorIdFromTag(Tag tag);
64 : friend constexpr uint16_t ProfileNumFromTag(Tag tag);
65 : friend constexpr uint32_t TagNumFromTag(Tag tag);
66 :
67 : friend constexpr bool IsProfileTag(Tag tag);
68 : friend constexpr bool IsContextTag(Tag tag);
69 : friend constexpr bool IsSpecialTag(Tag tag);
70 :
71 : static constexpr uint32_t kProfileIdShift = 32;
72 : static constexpr uint32_t kVendorIdShift = 48;
73 : static constexpr uint32_t kProfileNumShift = 32;
74 : static constexpr uint32_t kSpecialTagProfileId = 0xFFFFFFFF;
75 :
76 : // The storage of the tag value uses the following encoding:
77 : //
78 : // 63 47 31
79 : // +-------------------------------+-------------------------------+----------------------------------------------+
80 : // | Vendor id (bitwise-negated) | Profile num (bitwise-negated) | Tag number |
81 : // +-------------------------------+-------------------------------+----------------------------------------------+
82 : //
83 : // Vendor id and profile number are bitwise-negated in order to optimize the code size when
84 : // using context tags, the most commonly used tags in the SDK.
85 : uint64_t mVal;
86 : };
87 :
88 : enum TLVCommonProfiles
89 : {
90 : /**
91 : * Used to indicate the absence of a profile id in a variable or member.
92 : * This is essentially the same as kCHIPProfile_NotSpecified defined in CHIPProfiles.h
93 : */
94 : kProfileIdNotSpecified = 0xFFFFFFFF,
95 :
96 : // TODO: Replace with chip::Profiles::kCHIPProfile_Common
97 : kCommonProfileId = 0
98 : };
99 :
100 : // TODO: Move to private namespace
101 : enum class TLVTagControl : uint8_t
102 : {
103 : // IMPORTANT: All values here must have no bits in common with specified
104 : // values of TLVElementType.
105 : Anonymous = 0x00,
106 : ContextSpecific = 0x20,
107 : CommonProfile_2Bytes = 0x40,
108 : CommonProfile_4Bytes = 0x60,
109 : ImplicitProfile_2Bytes = 0x80,
110 : ImplicitProfile_4Bytes = 0xA0,
111 : FullyQualified_6Bytes = 0xC0,
112 : FullyQualified_8Bytes = 0xE0
113 : };
114 :
115 : template <typename T>
116 8815524 : inline uint8_t operator>>(TLVTagControl lhs, const T & rhs)
117 : {
118 8815524 : return static_cast<uint8_t>(static_cast<uint8_t>(lhs) >> rhs);
119 : }
120 :
121 : // TODO: Move to private namespace
122 : enum
123 : {
124 : kTLVTagControlMask = 0xE0,
125 : kTLVTagControlShift = 5
126 : };
127 :
128 : /**
129 : * Generates the API representation of a profile-specific TLV tag from a profile id and tag number
130 : *
131 : * @param[in] profileId The id of the profile within which the tag is defined.
132 : * @param[in] tagNum The profile-specific tag number assigned to the tag.
133 : * @return A 64-bit integer representing the tag.
134 : */
135 10378318 : constexpr Tag ProfileTag(uint32_t profileId, uint32_t tagNum)
136 : {
137 10378318 : return Tag((static_cast<uint64_t>(~profileId) << Tag::kProfileIdShift) | tagNum);
138 : }
139 :
140 : /**
141 : * Generates the API representation of a profile-specific TLV tag from a vendor id, profile number and tag number
142 : *
143 : * @param[in] vendorId The id of the vendor that defined the tag.
144 : * @param[in] profileNum The vendor assigned number for the profile within which the tag is defined.
145 : * @param[in] tagNum The profile-specific tag number assigned to the tag.
146 : * @return A 64-bit integer representing the tag.
147 : */
148 1939944 : constexpr Tag ProfileTag(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum)
149 : {
150 1939944 : constexpr uint32_t kVendorIdShift = Tag::kVendorIdShift - Tag::kProfileIdShift;
151 :
152 1939944 : return ProfileTag((static_cast<uint32_t>(vendorId) << kVendorIdShift) | profileNum, tagNum);
153 : }
154 :
155 : /**
156 : * Generates the API representation of a context-specific TLV tag
157 : *
158 : * @param[in] tagNum The context-specific tag number assigned to the tag.
159 : * @return A 64-bit integer representing the tag.
160 : */
161 2707725 : constexpr Tag ContextTag(uint8_t tagNum)
162 : {
163 2707725 : return ProfileTag(Tag::kSpecialTagProfileId, tagNum);
164 : }
165 :
166 : /**
167 : * Also allow using enum class values as context tags. There are four possible
168 : * cases when is_enum is true:
169 : *
170 : * 1) The enum is a scoped enum (enum class) and the underlying type is
171 : * uint8_t. Then the is_convertible test will test false, this overload will be
172 : * used, to_underlying will return a uint8_t, and everything will work.
173 : * 2) The enum is a scoped enum (enum class) and the underlying type is
174 : * not uint8_t. Then the is_convertible test will test false, this overload will be
175 : * used, to_underlying will return the other type, and -Wconversion will
176 : * catch the type mismatch, if it's enabled.
177 : * 3) The enum is an old-style enum. Then the is_convertible test will test
178 : * true, this overload will be not be used, and the uint8_t overload will be
179 : * used instead. The compiler should then catch (at least with sufficient
180 : * warnings turned on) if the constant being passed in is too big to fit into
181 : * uint8_t.
182 : *
183 : * Leaving out the is_convertible test, so this overload gets used for old-style
184 : * enums, would cause old-style enums where the underlying type is wider than
185 : * uint8_t to fail compilation with -Wconversion even if the values are all
186 : * inside the uint8_t range, since to_underlying would explicitly return a type
187 : * that is wider than uint8_t.
188 : */
189 : template <typename T, std::enable_if_t<std::is_enum<T>::value && !std::is_convertible<T, uint8_t>::value, int> = 0>
190 169064 : constexpr Tag ContextTag(T tagNum)
191 : {
192 169064 : return ContextTag(to_underlying(tagNum));
193 : }
194 :
195 : /**
196 : * Generates the API representation of a common profile TLV tag
197 : *
198 : * @param[in] tagNum The common profile tag number assigned to the tag.
199 : * @return A 64-bit integer representing the tag.
200 : */
201 127 : constexpr Tag CommonTag(uint32_t tagNum)
202 : {
203 127 : return ProfileTag(kCommonProfileId, tagNum);
204 : }
205 :
206 : /**
207 : * A value signifying a TLV element that has no tag (i.e. an anonymous element).
208 : */
209 5051460 : constexpr Tag AnonymousTag()
210 : {
211 5051460 : return ProfileTag(Tag::kSpecialTagProfileId, Tag::kAnonymousTagNum);
212 : }
213 :
214 : /**
215 : * An invalid tag that represents a TLV element decoding error due to unknown implicit profile id.
216 : */
217 7820458 : constexpr Tag UnknownImplicitTag()
218 : {
219 7820458 : return ProfileTag(Tag::kSpecialTagProfileId, Tag::kUnknownImplicitTagNum);
220 : }
221 :
222 : /**
223 : * Returns the profile id from a TLV tag
224 : *
225 : * @note The behavior of this function is undefined if the supplied tag is not a profile-specific tag.
226 : *
227 : * @param[in] tag The API representation of a profile-specific TLV tag.
228 : * @return The profile id.
229 : */
230 1712645 : constexpr uint32_t ProfileIdFromTag(Tag tag)
231 : {
232 1712645 : return ~static_cast<uint32_t>(tag.mVal >> Tag::kProfileIdShift);
233 : }
234 :
235 : /**
236 : * Returns the vendor id from a TLV tag
237 : *
238 : * @note The behavior of this function is undefined if the supplied tag is not a profile-specific tag.
239 : *
240 : * @param[in] tag The API representation of a profile-specific TLV tag.
241 : * @return The associated vendor id.
242 : */
243 36 : constexpr uint16_t VendorIdFromTag(Tag tag)
244 : {
245 36 : constexpr uint32_t kVendorIdShift = Tag::kVendorIdShift - Tag::kProfileIdShift;
246 :
247 36 : return static_cast<uint16_t>(ProfileIdFromTag(tag) >> kVendorIdShift);
248 : }
249 :
250 : /**
251 : * Returns the profile number from a TLV tag
252 : *
253 : * @note The behavior of this function is undefined if the supplied tag is not a profile-specific tag.
254 : *
255 : * @param[in] tag The API representation of a profile-specific TLV tag.
256 : * @return The associated profile number.
257 : */
258 16 : constexpr uint16_t ProfileNumFromTag(Tag tag)
259 : {
260 16 : return static_cast<uint16_t>(ProfileIdFromTag(tag));
261 : }
262 :
263 : /**
264 : * Returns the tag number from a TLV tag
265 : *
266 : * @note The behavior of this function is undefined if the supplied tag is not a profile-specific
267 : * or context-specific tag.
268 : *
269 : * @sa IsProfileTag() and IsContextTag()
270 : *
271 : * @param[in] tag The API representation of a profile-specific or context-specific TLV tag.
272 : * @return The associated tag number.
273 : */
274 1023355 : constexpr uint32_t TagNumFromTag(Tag tag)
275 : {
276 1023355 : return static_cast<uint32_t>(tag.mVal);
277 : }
278 :
279 : /**
280 : * Returns true of the supplied tag is a profile-specific tag.
281 : */
282 8236 : constexpr bool IsProfileTag(Tag tag)
283 : {
284 8236 : return ProfileIdFromTag(tag) != Tag::kSpecialTagProfileId;
285 : }
286 :
287 : /**
288 : * Returns true if the supplied tag is a context-specific tag.
289 : */
290 1332982 : constexpr bool IsContextTag(Tag tag)
291 : {
292 1332982 : return ProfileIdFromTag(tag) == Tag::kSpecialTagProfileId && TagNumFromTag(tag) <= Tag::kContextTagMaxNum;
293 : }
294 :
295 : // TODO: move to private namespace
296 367527 : constexpr bool IsSpecialTag(Tag tag)
297 : {
298 367527 : return ProfileIdFromTag(tag) == Tag::kSpecialTagProfileId;
299 : }
300 :
301 : } // namespace TLV
302 : } // namespace chip
|