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