Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-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 : #pragma once
20 :
21 : #include <app/data-model/FabricScoped.h>
22 : #include <app/data-model/Nullable.h>
23 : #include <lib/core/CHIPConfig.h>
24 : #include <lib/core/DataModelTypes.h>
25 : #include <lib/core/Optional.h>
26 : #include <lib/core/TLV.h>
27 : #include <protocols/interaction_model/Constants.h>
28 :
29 : #include <type_traits>
30 :
31 : namespace chip {
32 : namespace app {
33 : namespace DataModel {
34 :
35 : namespace detail {
36 : // A way to detect whether an enum has a kUnknownEnumValue value, for use in enable_if.
37 : template <typename Enum, Enum value>
38 : using VoidType = void;
39 :
40 : template <typename, typename = void>
41 : inline constexpr bool HasUnknownValue = false;
42 :
43 : template <typename T>
44 : inline constexpr bool HasUnknownValue<T, VoidType<T, T::kUnknownEnumValue>> = true;
45 : } // namespace detail
46 :
47 : /*
48 : * @brief
49 : * Set of overloaded encode methods that based on the type of cluster element passed in,
50 : * appropriately encodes them to TLV.
51 : */
52 : template <typename X, typename std::enable_if_t<std::is_integral<X>::value, int> = 0>
53 14462 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, X x)
54 : {
55 14462 : return writer.Put(tag, x);
56 : }
57 :
58 : template <typename X, typename std::enable_if_t<std::is_floating_point<X>::value, int> = 0>
59 100 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, X x)
60 : {
61 100 : return writer.Put(tag, x);
62 : }
63 :
64 : template <typename X, typename std::enable_if_t<std::is_enum<X>::value && !detail::HasUnknownValue<X>, int> = 0>
65 0 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, X x)
66 : {
67 0 : return writer.Put(tag, x);
68 : }
69 :
70 : // Reusable macro for dealing with unknown enum values that we can use in
71 : // attribute value encoding.
72 : #if CHIP_CONFIG_IM_ENABLE_ENCODING_SENTINEL_ENUM_VALUES
73 : #define CHIP_DM_ENCODING_MAYBE_FAIL_UNKNOWN_ENUM_VALUE(value) \
74 : do \
75 : { \
76 : /* Nothing to do */ \
77 : } while (0)
78 : #else
79 : #define CHIP_DM_ENCODING_MAYBE_FAIL_UNKNOWN_ENUM_VALUE(value) \
80 : do \
81 : { \
82 : if (value == std::remove_reference_t<decltype(value)>::kUnknownEnumValue) \
83 : { \
84 : return CHIP_IM_GLOBAL_STATUS(ConstraintError); \
85 : } \
86 : } while (0)
87 : #endif // CHIP_CONFIG_IM_ENABLE_ENCODING_SENTINEL_ENUM_VALUES
88 :
89 : template <typename X, typename std::enable_if_t<std::is_enum<X>::value && detail::HasUnknownValue<X>, int> = 0>
90 51 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, X x)
91 : {
92 51 : CHIP_DM_ENCODING_MAYBE_FAIL_UNKNOWN_ENUM_VALUE(x);
93 :
94 51 : return writer.Put(tag, x);
95 : }
96 :
97 : template <typename X>
98 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, BitFlags<X> x)
99 : {
100 : return writer.Put(tag, x);
101 : }
102 :
103 14281 : inline CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, ByteSpan x)
104 : {
105 14281 : return writer.Put(tag, x);
106 : }
107 :
108 119 : inline CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, Span<const char> x)
109 : {
110 119 : return writer.PutString(tag, x);
111 : }
112 :
113 : /*
114 : * @brief
115 : *
116 : * This specific variant that encodes cluster objects (like non fabric-scoped structs, commands, events) to TLV
117 : * depends on the presence of an Encode method on the object. The signature of that method
118 : * is as follows:
119 : *
120 : * CHIP_ERROR <Object>::Encode(TLVWriter &writer, TLV::Tag tag) const;
121 : *
122 : *
123 : */
124 : template <typename X,
125 : typename std::enable_if_t<
126 : std::is_class<X>::value &&
127 : std::is_same<decltype(std::declval<X>().Encode(std::declval<TLV::TLVWriter &>(), std::declval<TLV::Tag>())),
128 : CHIP_ERROR>::value,
129 : X> * = nullptr>
130 6490 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, const X & x)
131 : {
132 6490 : return x.Encode(writer, tag);
133 : }
134 :
135 : /*
136 : * @brief
137 : *
138 : * A way to encode fabric-scoped structs for a write that omits encoding the containing fabric index field.
139 : */
140 : template <typename X,
141 : typename std::enable_if_t<std::is_class<X>::value &&
142 : std::is_same<decltype(std::declval<X>().EncodeForWrite(std::declval<TLV::TLVWriter &>(),
143 : std::declval<TLV::Tag>())),
144 : CHIP_ERROR>::value &&
145 : DataModel::IsFabricScoped<X>::value,
146 : X> * = nullptr>
147 8 : CHIP_ERROR EncodeForWrite(TLV::TLVWriter & writer, TLV::Tag tag, const X & x)
148 : {
149 8 : return x.EncodeForWrite(writer, tag);
150 : }
151 :
152 : /*
153 : * @brief
154 : *
155 : * A way to encode fabric-scoped structs for a read that always encodes the containing fabric index field.
156 : *
157 : * An accessing fabric index must be passed in to permit including/omitting sensitive fields based on a match with the fabric index
158 : * associated with the scoped struct.
159 : */
160 : template <typename X,
161 : typename std::enable_if_t<
162 : std::is_class<X>::value &&
163 : std::is_same<decltype(std::declval<X>().EncodeForRead(std::declval<TLV::TLVWriter &>(), std::declval<TLV::Tag>(),
164 : std::declval<FabricIndex>())),
165 : CHIP_ERROR>::value &&
166 : DataModel::IsFabricScoped<X>::value,
167 : X> * = nullptr>
168 81 : CHIP_ERROR EncodeForRead(TLV::TLVWriter & writer, TLV::Tag tag, FabricIndex accessingFabricIndex, const X & x)
169 : {
170 81 : return x.EncodeForRead(writer, tag, accessingFabricIndex);
171 : }
172 :
173 : /*
174 : * @brief
175 : *
176 : * Encodes an optional value (struct field, command field, event field).
177 : */
178 : template <typename X>
179 0 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, const Optional<X> & x)
180 : {
181 0 : if (x.HasValue())
182 : {
183 0 : return Encode(writer, tag, x.Value());
184 : }
185 : // If no value, just do nothing.
186 0 : return CHIP_NO_ERROR;
187 : }
188 :
189 : /*
190 : * @brief
191 : *
192 : * Encodes a nullable value.
193 : */
194 : template <typename X>
195 11270 : CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, const Nullable<X> & x)
196 : {
197 11270 : if (x.IsNull())
198 : {
199 11225 : return writer.PutNull(tag);
200 : }
201 :
202 : // Allow sending invalid values for nullables when
203 : // CONFIG_BUILD_FOR_HOST_UNIT_TEST is true, so we can test how the other side
204 : // responds.
205 : #if !CONFIG_BUILD_FOR_HOST_UNIT_TEST
206 : if (!x.ExistingValueInEncodableRange())
207 : {
208 : return CHIP_IM_GLOBAL_STATUS(ConstraintError);
209 : }
210 : #endif // !CONFIG_BUILD_FOR_HOST_UNIT_TEST
211 :
212 : // The -Wmaybe-uninitialized warning gets confused about the fact
213 : // that x.mValue is always initialized if x.IsNull() is not
214 : // true, so suppress it for our access to x.Value().
215 : #pragma GCC diagnostic push
216 : #if !defined(__clang__)
217 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
218 : #endif // !defined(__clang__)
219 45 : return Encode(writer, tag, x.Value());
220 : #pragma GCC diagnostic pop
221 : }
222 :
223 : /*
224 : * @brief
225 : *
226 : * Encodes a nullable fabric-scoped struct for a write.
227 : */
228 : template <typename X, std::enable_if_t<DataModel::IsFabricScoped<X>::value, bool> = true>
229 : CHIP_ERROR EncodeForWrite(TLV::TLVWriter & writer, TLV::Tag tag, const Nullable<X> & x)
230 : {
231 : if (x.IsNull())
232 : {
233 : return writer.PutNull(tag);
234 : }
235 :
236 : // The -Wmaybe-uninitialized warning gets confused about the fact
237 : // that x.mValue is always initialized if x.IsNull() is not
238 : // true, so suppress it for our access to x.Value().
239 : #pragma GCC diagnostic push
240 : #if !defined(__clang__)
241 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
242 : #endif // !defined(__clang__)
243 : return EncodeForWrite(writer, tag, x.Value());
244 : #pragma GCC diagnostic pop
245 : }
246 :
247 : /*
248 : * @brief
249 : *
250 : * Encodes a nullable fabric-scoped struct for a read.
251 : */
252 : template <typename X, std::enable_if_t<DataModel::IsFabricScoped<X>::value, bool> = true>
253 : CHIP_ERROR EncodeForRead(TLV::TLVWriter & writer, TLV::Tag tag, FabricIndex accessingFabricIndex, const Nullable<X> & x)
254 : {
255 : if (x.IsNull())
256 : {
257 : return writer.PutNull(tag);
258 : }
259 :
260 : // The -Wmaybe-uninitialized warning gets confused about the fact
261 : // that x.mValue is always initialized if x.IsNull() is not
262 : // true, so suppress it for our access to x.Value().
263 : #pragma GCC diagnostic push
264 : #if !defined(__clang__)
265 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
266 : #endif // !defined(__clang__)
267 : return EncodeForRead(writer, tag, accessingFabricIndex, x.Value());
268 : #pragma GCC diagnostic pop
269 : }
270 :
271 : // Should this be declared in a separate header?
272 : struct FabricAwareTLVWriter
273 : {
274 24 : FabricAwareTLVWriter(TLV::TLVWriter & writer, FabricIndex accessingFabricIndex) :
275 24 : mTLVWriter(writer), mAccessingFabricIndex(accessingFabricIndex)
276 24 : {}
277 :
278 19 : operator TLV::TLVWriter &() { return mTLVWriter; }
279 :
280 : TLV::TLVWriter & mTLVWriter;
281 : const FabricIndex mAccessingFabricIndex;
282 : };
283 :
284 : /**
285 : * @brief
286 : *
287 : * Encodes a response command payload. This is a templated function to allow
288 : * specializations to be created as needed to customize the behavior.
289 : */
290 : template <typename PayloadType>
291 11 : CHIP_ERROR EncodeResponseCommandPayload(FabricAwareTLVWriter & writer, TLV::Tag tag, const PayloadType & payload)
292 : {
293 11 : return payload.Encode(writer, tag);
294 : }
295 :
296 : } // namespace DataModel
297 : } // namespace app
298 : } // namespace chip
|