Line data Source code
1 : /*
2 : * Copyright (c) 2024 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #include <data-model-providers/codegen/EmberAttributeDataBuffer.h>
18 :
19 : #include <app-common/zap-generated/attribute-type.h>
20 : #include <app/AttributeValueEncoder.h>
21 : #include <app/util/attribute-metadata.h>
22 : #include <app/util/attribute-storage-null-handling.h>
23 : #include <app/util/odd-sized-integers.h>
24 : #include <lib/core/CHIPError.h>
25 : #include <lib/core/TLVTags.h>
26 : #include <lib/core/TLVTypes.h>
27 : #include <lib/core/TLVWriter.h>
28 : #include <lib/support/CodeUtils.h>
29 : #include <protocols/interaction_model/Constants.h>
30 : #include <protocols/interaction_model/StatusCode.h>
31 :
32 : #include <cstdint>
33 : #include <limits>
34 :
35 : namespace chip {
36 : namespace app {
37 : namespace Ember {
38 :
39 : namespace {
40 :
41 : /// Maximum length of a string, inclusive
42 : ///
43 : /// the max size value (0xFF and 0xFFFF) is reserved for NULL representation so
44 : /// it is not available
45 31 : constexpr uint32_t MaxLength(EmberAttributeDataBuffer::PascalStringType s)
46 : {
47 31 : if (s == EmberAttributeDataBuffer::PascalStringType::kShort)
48 : {
49 14 : return std::numeric_limits<uint8_t>::max() - 1;
50 : }
51 : // EmberAttributeDataBuffer::PascalStringType::kLong:
52 17 : return std::numeric_limits<uint16_t>::max() - 1;
53 : }
54 :
55 331 : constexpr unsigned GetByteCountOfIntegerType(EmberAfAttributeType type)
56 : {
57 : // This TERRIBLE bit fiddling, however it is small in flash
58 : // and we assert statically the actual values we care about
59 :
60 : // ZCL_INT8U_ATTRIBUTE_TYPE = 0x20, // Unsigned 8-bit integer
61 : // ZCL_INT16U_ATTRIBUTE_TYPE = 0x21, // Unsigned 16-bit integer
62 : // ZCL_INT24U_ATTRIBUTE_TYPE = 0x22, // Unsigned 24-bit integer
63 : // ZCL_INT32U_ATTRIBUTE_TYPE = 0x23, // Unsigned 32-bit integer
64 : // ZCL_INT40U_ATTRIBUTE_TYPE = 0x24, // Unsigned 40-bit integer
65 : // ZCL_INT48U_ATTRIBUTE_TYPE = 0x25, // Unsigned 48-bit integer
66 : // ZCL_INT56U_ATTRIBUTE_TYPE = 0x26, // Unsigned 56-bit integer
67 : // ZCL_INT64U_ATTRIBUTE_TYPE = 0x27, // Unsigned 64-bit integer
68 : //
69 : // ZCL_INT8S_ATTRIBUTE_TYPE = 0x28, // Signed 8-bit integer
70 : // ZCL_INT16S_ATTRIBUTE_TYPE = 0x29, // Signed 16-bit integer
71 : // ZCL_INT24S_ATTRIBUTE_TYPE = 0x2A, // Signed 24-bit integer
72 : // ZCL_INT32S_ATTRIBUTE_TYPE = 0x2B, // Signed 32-bit integer
73 : // ZCL_INT40S_ATTRIBUTE_TYPE = 0x2C, // Signed 40-bit integer
74 : // ZCL_INT48S_ATTRIBUTE_TYPE = 0x2D, // Signed 48-bit integer
75 : // ZCL_INT56S_ATTRIBUTE_TYPE = 0x2E, // Signed 56-bit integer
76 : // ZCL_INT64S_ATTRIBUTE_TYPE = 0x2F, // Signed 64-bit integer
77 :
78 331 : return (static_cast<unsigned>(type) % 8) + 1;
79 : }
80 : static_assert(GetByteCountOfIntegerType(ZCL_INT8U_ATTRIBUTE_TYPE) == 1);
81 : static_assert(GetByteCountOfIntegerType(ZCL_INT8S_ATTRIBUTE_TYPE) == 1);
82 : static_assert(GetByteCountOfIntegerType(ZCL_INT16U_ATTRIBUTE_TYPE) == 2);
83 : static_assert(GetByteCountOfIntegerType(ZCL_INT16S_ATTRIBUTE_TYPE) == 2);
84 : static_assert(GetByteCountOfIntegerType(ZCL_INT24U_ATTRIBUTE_TYPE) == 3);
85 : static_assert(GetByteCountOfIntegerType(ZCL_INT24S_ATTRIBUTE_TYPE) == 3);
86 : static_assert(GetByteCountOfIntegerType(ZCL_INT32U_ATTRIBUTE_TYPE) == 4);
87 : static_assert(GetByteCountOfIntegerType(ZCL_INT32S_ATTRIBUTE_TYPE) == 4);
88 : static_assert(GetByteCountOfIntegerType(ZCL_INT40U_ATTRIBUTE_TYPE) == 5);
89 : static_assert(GetByteCountOfIntegerType(ZCL_INT40S_ATTRIBUTE_TYPE) == 5);
90 : static_assert(GetByteCountOfIntegerType(ZCL_INT48U_ATTRIBUTE_TYPE) == 6);
91 : static_assert(GetByteCountOfIntegerType(ZCL_INT48S_ATTRIBUTE_TYPE) == 6);
92 : static_assert(GetByteCountOfIntegerType(ZCL_INT56U_ATTRIBUTE_TYPE) == 7);
93 : static_assert(GetByteCountOfIntegerType(ZCL_INT56S_ATTRIBUTE_TYPE) == 7);
94 : static_assert(GetByteCountOfIntegerType(ZCL_INT64U_ATTRIBUTE_TYPE) == 8);
95 : static_assert(GetByteCountOfIntegerType(ZCL_INT64S_ATTRIBUTE_TYPE) == 8);
96 :
97 : /// Encodes the string of type stringType pointed to by `reader` into the TLV `writer`.
98 : /// Then encoded string will be at tag `tag` and of type `tlvType`
99 28 : CHIP_ERROR EncodeString(EmberAttributeDataBuffer::PascalStringType stringType, TLV::TLVType tlvType, TLV::TLVWriter & writer,
100 : TLV::Tag tag, EmberAttributeDataBuffer::EndianReader & reader, bool nullable)
101 : {
102 : unsigned stringLen;
103 28 : if (stringType == EmberAttributeDataBuffer::PascalStringType::kShort)
104 : {
105 : uint8_t len;
106 15 : if (!reader.Read8(&len).IsSuccess())
107 : {
108 2 : return reader.StatusCode();
109 : }
110 15 : if (len == NumericAttributeTraits<uint8_t>::kNullValue)
111 : {
112 2 : VerifyOrReturnError(nullable, CHIP_ERROR_INVALID_ARGUMENT);
113 2 : return writer.PutNull(tag);
114 : }
115 13 : stringLen = len;
116 : }
117 : else
118 : {
119 : uint16_t len;
120 13 : if (!reader.Read16(&len).IsSuccess())
121 : {
122 3 : return reader.StatusCode();
123 : }
124 13 : if (len == NumericAttributeTraits<uint16_t>::kNullValue)
125 : {
126 3 : VerifyOrReturnError(nullable, CHIP_ERROR_INVALID_ARGUMENT);
127 3 : return writer.PutNull(tag);
128 : }
129 10 : stringLen = len;
130 : }
131 :
132 : const uint8_t * data;
133 23 : if (!reader.ZeroCopyProcessBytes(stringLen, &data).IsSuccess())
134 : {
135 2 : return reader.StatusCode();
136 : }
137 :
138 21 : if (tlvType == TLV::kTLVType_UTF8String)
139 : {
140 11 : return writer.PutString(tag, reinterpret_cast<const char *>(data), stringLen);
141 : }
142 :
143 10 : return writer.PutBytes(tag, data, stringLen);
144 : }
145 :
146 : } // namespace
147 :
148 87 : CHIP_ERROR EmberAttributeDataBuffer::DecodeUnsignedInteger(chip::TLV::TLVReader & reader, EndianWriter & writer)
149 : {
150 87 : const unsigned byteCount = GetByteCountOfIntegerType(mAttributeType);
151 87 : const uint64_t maxValue = NumericLimits::MaxUnsignedValue(byteCount);
152 :
153 : // Any size of integer can be read by TLV getting 64-bit integers
154 : uint64_t value;
155 :
156 87 : if (reader.GetType() == TLV::kTLVType_Null)
157 : {
158 : // we know mIsNullable due to the check at the top of ::Decode
159 22 : value = NumericLimits::UnsignedMaxValueToNullValue(maxValue);
160 : }
161 : else
162 : {
163 65 : ReturnErrorOnFailure(reader.Get(value));
164 :
165 : bool valid =
166 : // Value is in [0, max] RANGE
167 64 : (value <= maxValue)
168 : // Nullable values reserve a specific value to mean NULL
169 64 : && !(mIsNullable && (value == NumericLimits::UnsignedMaxValueToNullValue(maxValue)));
170 :
171 64 : VerifyOrReturnError(valid, CHIP_IM_GLOBAL_STATUS(ConstraintError));
172 : }
173 :
174 74 : writer.EndianPut(value, byteCount);
175 74 : return CHIP_NO_ERROR;
176 : }
177 :
178 123 : CHIP_ERROR EmberAttributeDataBuffer::DecodeSignedInteger(chip::TLV::TLVReader & reader, EndianWriter & writer)
179 : {
180 123 : const unsigned byteCount = GetByteCountOfIntegerType(mAttributeType);
181 123 : const int64_t minValue = NumericLimits::MinSignedValue(byteCount);
182 123 : const int64_t maxValue = NumericLimits::MaxSignedValue(byteCount);
183 :
184 : // Any size of integer can be read by TLV getting 64-bit integers
185 : int64_t value;
186 :
187 123 : if (reader.GetType() == TLV::kTLVType_Null)
188 : {
189 : // we know mIsNullable due to the check at the top of ::Decode
190 27 : value = NumericLimits::SignedMinValueToNullValue(minValue);
191 : }
192 : else
193 : {
194 96 : ReturnErrorOnFailure(reader.Get(value));
195 :
196 : bool valid =
197 : // Value is in [min, max] RANGE
198 92 : ((value >= minValue) && (value <= maxValue))
199 : // Nullable values reserve a specific value to mean NULL
200 188 : && !(mIsNullable && (value == NumericLimits::SignedMinValueToNullValue(minValue)));
201 :
202 96 : VerifyOrReturnError(valid, CHIP_IM_GLOBAL_STATUS(ConstraintError));
203 : }
204 110 : writer.EndianPutSigned(value, byteCount);
205 110 : return CHIP_NO_ERROR;
206 : }
207 :
208 39 : CHIP_ERROR EmberAttributeDataBuffer::DecodeAsString(chip::TLV::TLVReader & reader, PascalStringType stringType,
209 : TLV::TLVType tlvType, EndianWriter & writer)
210 : {
211 : // Handle null first, then the actual data
212 39 : if (reader.GetType() == TLV::kTLVType_Null)
213 : {
214 : // we know mIsNullable due to the check at the top of ::Decode
215 7 : switch (stringType)
216 : {
217 3 : case PascalStringType::kShort:
218 3 : writer.Put8(NumericAttributeTraits<uint8_t>::kNullValue);
219 3 : break;
220 4 : case PascalStringType::kLong:
221 4 : writer.Put16(NumericAttributeTraits<uint16_t>::kNullValue);
222 4 : break;
223 : }
224 :
225 7 : return CHIP_NO_ERROR;
226 : }
227 :
228 32 : const uint32_t stringLength = reader.GetLength();
229 :
230 32 : VerifyOrReturnError(reader.GetType() == tlvType, CHIP_ERROR_WRONG_TLV_TYPE);
231 31 : VerifyOrReturnError(stringLength <= MaxLength(stringType), CHIP_ERROR_INVALID_ARGUMENT);
232 :
233 : // Size is a prefix, where 0xFF/0xFFFF is the null marker (if applicable)
234 31 : switch (stringType)
235 : {
236 14 : case PascalStringType::kShort:
237 14 : writer.Put8(static_cast<uint8_t>(stringLength));
238 14 : break;
239 17 : case PascalStringType::kLong:
240 17 : writer.Put16(static_cast<uint16_t>(stringLength));
241 17 : break;
242 : }
243 :
244 : // data copy
245 : const uint8_t * tlvData;
246 31 : ReturnErrorOnFailure(reader.GetDataPtr(tlvData));
247 31 : writer.Put(tlvData, stringLength);
248 :
249 31 : return CHIP_NO_ERROR;
250 : }
251 :
252 297 : CHIP_ERROR EmberAttributeDataBuffer::Decode(chip::TLV::TLVReader & reader)
253 : {
254 : // all methods below assume that nullable setting matches (this is to reduce code size
255 : // even though clarity suffers)
256 297 : VerifyOrReturnError(mIsNullable || reader.GetType() != TLV::kTLVType_Null, CHIP_ERROR_WRONG_TLV_TYPE);
257 :
258 277 : EndianWriter endianWriter(mDataBuffer.data(), mDataBuffer.size());
259 :
260 277 : switch (mAttributeType)
261 : {
262 12 : case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean
263 : // Boolean values:
264 : // 0x00 is FALSE
265 : // 0x01 is TRUE
266 : // 0xFF is NULL
267 12 : if (reader.GetType() == TLV::kTLVType_Null)
268 : {
269 : // we know mIsNullable due to the check at the top of ::Decode
270 4 : endianWriter.Put8(NumericAttributeTraits<bool>::kNullValue);
271 : }
272 : else
273 : {
274 : bool value;
275 8 : ReturnErrorOnFailure(reader.Get(value));
276 8 : endianWriter.Put8(value ? 1 : 0);
277 : }
278 12 : break;
279 87 : case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
280 : case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
281 : case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
282 : case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
283 : case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
284 : case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
285 : case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
286 : case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
287 87 : ReturnErrorOnFailure(DecodeUnsignedInteger(reader, endianWriter));
288 74 : break;
289 123 : case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
290 : case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
291 : case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
292 : case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
293 : case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
294 : case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
295 : case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
296 : case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
297 123 : ReturnErrorOnFailure(DecodeSignedInteger(reader, endianWriter));
298 110 : break;
299 7 : case ZCL_SINGLE_ATTRIBUTE_TYPE: { // 32-bit float
300 : float value;
301 7 : if (reader.GetType() == TLV::kTLVType_Null)
302 : {
303 : // we know mIsNullable due to the check at the top of ::Decode
304 3 : NumericAttributeTraits<float>::SetNull(value);
305 : }
306 : else
307 : {
308 :
309 4 : ReturnErrorOnFailure(reader.Get(value));
310 : }
311 7 : endianWriter.Put(&value, sizeof(value));
312 7 : break;
313 : }
314 7 : case ZCL_DOUBLE_ATTRIBUTE_TYPE: { // 64-bit float
315 : double value;
316 7 : if (reader.GetType() == TLV::kTLVType_Null)
317 : {
318 : // we know mIsNullable due to the check at the top of ::Decode
319 3 : NumericAttributeTraits<double>::SetNull(value);
320 : }
321 : else
322 : {
323 4 : ReturnErrorOnFailure(reader.Get(value));
324 : }
325 7 : endianWriter.Put(&value, sizeof(value));
326 7 : break;
327 : }
328 12 : case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
329 12 : ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kShort, TLV::kTLVType_UTF8String, endianWriter));
330 11 : break;
331 15 : case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:
332 15 : ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kLong, TLV::kTLVType_UTF8String, endianWriter));
333 15 : break;
334 6 : case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
335 6 : ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kShort, TLV::kTLVType_ByteString, endianWriter));
336 6 : break;
337 6 : case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE:
338 6 : ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kLong, TLV::kTLVType_ByteString, endianWriter));
339 6 : break;
340 2 : default:
341 2 : ChipLogError(DataManagement, "Attribute type 0x%x not handled", mAttributeType);
342 2 : return CHIP_IM_GLOBAL_STATUS(Failure);
343 : }
344 :
345 : size_t written;
346 248 : if (!endianWriter.Fit(written))
347 : {
348 6 : return CHIP_ERROR_NO_MEMORY;
349 : }
350 :
351 242 : mDataBuffer.reduce_size(written);
352 242 : return CHIP_NO_ERROR;
353 : }
354 :
355 121 : CHIP_ERROR EmberAttributeDataBuffer::EncodeInteger(chip::TLV::TLVWriter & writer, TLV::Tag tag, EndianReader & reader) const
356 : {
357 : // Encodes an integer by first reading as raw bytes and then
358 : // bitshift-convert
359 : //
360 : // This optimizes code size rather than readability at this point.
361 :
362 : uint8_t raw_bytes[8];
363 :
364 121 : const bool isSigned = IsSignedAttributeType(mAttributeType);
365 121 : const unsigned byteCount = GetByteCountOfIntegerType(mAttributeType);
366 :
367 : const uint64_t nullValueAsU64 = isSigned
368 121 : ? static_cast<uint64_t>(NumericLimits::SignedMinValueToNullValue(NumericLimits::MinSignedValue(byteCount)))
369 59 : : NumericLimits::UnsignedMaxValueToNullValue(NumericLimits::MaxUnsignedValue(byteCount));
370 :
371 121 : VerifyOrDie(sizeof(raw_bytes) >= byteCount);
372 121 : if (!reader.ReadBytes(raw_bytes, byteCount).IsSuccess())
373 : {
374 1 : return reader.StatusCode();
375 : }
376 :
377 : // At this point, RAW_VALUE contains the actual value, need to make it "real"
378 : union
379 : {
380 : int64_t int_value;
381 : uint64_t uint_value;
382 : } value;
383 :
384 120 : value.uint_value = 0;
385 :
386 : #if CHIP_CONFIG_BIG_ENDIAN_TARGET
387 : bool isNegative = isSigned && (raw_bytes[0] >= 0x80);
388 : if (isNegative)
389 : {
390 : value.int_value = -1;
391 : }
392 : for (int i = 0; i < static_cast<int>(byteCount); i++)
393 : {
394 : #else
395 120 : bool isNegative = isSigned && (raw_bytes[byteCount - 1] >= 0x80);
396 120 : if (isNegative)
397 : {
398 42 : value.int_value = -1;
399 : }
400 554 : for (int i = static_cast<int>(byteCount) - 1; i >= 0; i--)
401 : {
402 : #endif
403 434 : value.uint_value <<= 8;
404 434 : value.uint_value = (value.uint_value & ~0xFFULL) | raw_bytes[i];
405 : }
406 :
407 : // We place the null value as either int_value or uint_value into a union that is
408 : // bit-formatted as both int64 and uint64. When we define the nullValue,
409 : // it is bitcast into u64 hence this comparison. This is ugly, however this
410 : // code prioritizes code size over readability here.
411 120 : if (mIsNullable && (value.uint_value == nullValueAsU64))
412 : {
413 29 : return writer.PutNull(tag);
414 : }
415 :
416 91 : if (isSigned)
417 : {
418 49 : return writer.Put(tag, value.int_value);
419 : }
420 :
421 42 : return writer.Put(tag, value.uint_value);
422 : }
423 :
424 179 : CHIP_ERROR EmberAttributeDataBuffer::Encode(chip::TLV::TLVWriter & writer, TLV::Tag tag) const
425 : {
426 179 : EndianReader endianReader(mDataBuffer.data(), mDataBuffer.size());
427 :
428 179 : switch (mAttributeType)
429 : {
430 3 : case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data
431 3 : return writer.PutNull(tag);
432 11 : case ZCL_BOOLEAN_ATTRIBUTE_TYPE: { // Boolean
433 : uint8_t value;
434 11 : if (!endianReader.Read8(&value).IsSuccess())
435 : {
436 0 : return endianReader.StatusCode();
437 : }
438 11 : switch (value)
439 : {
440 6 : case 0:
441 : case 1:
442 6 : return writer.PutBoolean(tag, value != 0);
443 4 : case 0xFF:
444 4 : VerifyOrReturnError(mIsNullable, CHIP_ERROR_INVALID_ARGUMENT);
445 2 : return writer.PutNull(tag);
446 1 : default:
447 : // Unknown types
448 1 : return CHIP_ERROR_INVALID_ARGUMENT;
449 : }
450 : }
451 121 : case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
452 : case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
453 : case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
454 : case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
455 : case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
456 : case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
457 : case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
458 : case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
459 : case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
460 : case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
461 : case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
462 : case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
463 : case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
464 : case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
465 : case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
466 : case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
467 121 : return EncodeInteger(writer, tag, endianReader);
468 7 : case ZCL_SINGLE_ATTRIBUTE_TYPE: { // 32-bit float
469 : union
470 : {
471 : uint8_t raw[sizeof(float)];
472 : float value;
473 : } value;
474 :
475 7 : if (!endianReader.ReadBytes(value.raw, sizeof(value)).IsSuccess())
476 : {
477 1 : return endianReader.StatusCode();
478 : }
479 6 : if (mIsNullable && NumericAttributeTraits<float>::IsNullValue(value.value))
480 : {
481 2 : return writer.PutNull(tag);
482 : }
483 4 : return writer.Put(tag, value.value);
484 : }
485 8 : case ZCL_DOUBLE_ATTRIBUTE_TYPE: { // 64-bit float
486 : union
487 : {
488 : uint8_t raw[sizeof(double)];
489 : double value;
490 : } value;
491 :
492 8 : if (!endianReader.ReadBytes(value.raw, sizeof(value)).IsSuccess())
493 : {
494 1 : return endianReader.StatusCode();
495 : }
496 7 : if (mIsNullable && NumericAttributeTraits<double>::IsNullValue(value.value))
497 : {
498 2 : return writer.PutNull(tag);
499 : }
500 5 : return writer.Put(tag, value.value);
501 : }
502 :
503 9 : case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
504 9 : return EncodeString(PascalStringType::kShort, TLV::kTLVType_UTF8String, writer, tag, endianReader, mIsNullable);
505 6 : case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:
506 6 : return EncodeString(PascalStringType::kLong, TLV::kTLVType_UTF8String, writer, tag, endianReader, mIsNullable);
507 6 : case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
508 6 : return EncodeString(PascalStringType::kShort, TLV::kTLVType_ByteString, writer, tag, endianReader, mIsNullable);
509 7 : case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE:
510 7 : return EncodeString(PascalStringType::kLong, TLV::kTLVType_ByteString, writer, tag, endianReader, mIsNullable);
511 1 : default:
512 1 : ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast<int>(mAttributeType));
513 1 : return CHIP_IM_GLOBAL_STATUS(Failure);
514 : }
515 : }
516 :
517 : } // namespace Ember
518 : } // namespace app
519 : } // namespace chip
|