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