Skip to content

Commit

Permalink
Use custom ember-buffer decode on codegen data model Read as well. (#…
Browse files Browse the repository at this point in the history
…36229)

* Use EmberAttributeDataBuffer for codegen provider _Read

* Fix comments

* Restyled by clang-format

---------

Co-authored-by: Andrei Litvin <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
3 people authored Oct 24, 2024
1 parent 1febc9b commit 692983e
Show file tree
Hide file tree
Showing 4 changed files with 822 additions and 176 deletions.
175 changes: 7 additions & 168 deletions src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <app/AttributeValueEncoder.h>
#include <app/GlobalAttributes.h>
#include <app/RequiredPrivilege.h>
#include <app/codegen-data-model-provider/EmberAttributeDataBuffer.h>
#include <app/codegen-data-model-provider/EmberMetadata.h>
#include <app/data-model/FabricScoped.h>
#include <app/util/af-types.h>
Expand All @@ -41,6 +42,7 @@
#include <app/util/odd-sized-integers.h>
#include <lib/core/CHIPError.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/Span.h>

#include <zap-generated/endpoint_config.h>

Expand Down Expand Up @@ -85,173 +87,6 @@ std::optional<CHIP_ERROR> TryReadViaAccessInterface(const ConcreteAttributePath
return encoder.TriedEncode() ? std::make_optional(CHIP_NO_ERROR) : std::nullopt;
}

/// Metadata of what a ember/pascal short string means (prepended by a u8 length)
struct ShortPascalString
{
using LengthType = uint8_t;
static constexpr LengthType kNullLength = 0xFF;

static size_t GetLength(ByteSpan buffer)
{
VerifyOrDie(buffer.size() >= 1);
// NOTE: we do NOT use emberAfStringLength from ember-strings.h because that will result in 0
// length for null sizes (i.e. 0xFF is translated to 0 and we do not want that here)
return buffer[0];
}
};

/// Metadata of what a ember/pascal LONG string means (prepended by a u16 length)
struct LongPascalString
{
using LengthType = uint16_t;
static constexpr LengthType kNullLength = 0xFFFF;

static size_t GetLength(ByteSpan buffer)
{
// NOTE: we do NOT use emberAfLongStringLength from ember-strings.h because that will result in 0
// length for null sizes (i.e. 0xFFFF is translated to 0 and we do not want that here)
VerifyOrDie(buffer.size() >= 2);
const uint8_t * data = buffer.data();
return Encoding::LittleEndian::Read16(data);
}
};

// ember assumptions ... should just work
static_assert(sizeof(ShortPascalString::LengthType) == 1);
static_assert(sizeof(LongPascalString::LengthType) == 2);

/// Given a ByteSpan containing data from ember, interpret it
/// as a span of type OUT (i.e. ByteSpan or CharSpan) given a ENCODING
/// where ENCODING is Short or Long pascal strings.
template <class OUT_TYPE, class ENCODING>
std::optional<OUT_TYPE> ExtractEmberString(ByteSpan data)
{
constexpr size_t kLengthTypeSize = sizeof(typename ENCODING::LengthType);
VerifyOrDie(kLengthTypeSize <= data.size());
auto len = ENCODING::GetLength(data);

if (len == ENCODING::kNullLength)
{
return std::nullopt;
}

VerifyOrDie(len + sizeof(len) <= data.size());
return std::make_optional<OUT_TYPE>(reinterpret_cast<typename OUT_TYPE::pointer>(data.data() + kLengthTypeSize), len);
}

/// Encode a value inside `encoder`
///
/// The value encoded will be of type T (e.g. CharSpan or ByteSpan) and it will be decoded
/// via the given ENCODING (i.e. ShortPascalString or LongPascalString)
///
/// isNullable defines if the value of NULL is allowed to be encoded.
template <typename T, class ENCODING>
CHIP_ERROR EncodeStringLike(ByteSpan data, bool isNullable, AttributeValueEncoder & encoder)
{
std::optional<T> value = ExtractEmberString<T, ENCODING>(data);
if (!value.has_value())
{
if (isNullable)
{
return encoder.EncodeNull();
}
return CHIP_ERROR_INCORRECT_STATE;
}

// encode value as-is
return encoder.Encode(*value);
}

/// Encodes a numeric data value of type T from the given ember-encoded buffer `data`.
///
/// isNullable defines if the value of NULL is allowed to be encoded.
template <typename T>
CHIP_ERROR EncodeFromSpan(ByteSpan data, bool isNullable, AttributeValueEncoder & encoder)
{
typename NumericAttributeTraits<T>::StorageType value;

VerifyOrReturnError(data.size() >= sizeof(value), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(&value, data.data(), sizeof(value));

if (isNullable && NumericAttributeTraits<T>::IsNullValue(value))
{
return encoder.EncodeNull();
}

if (!NumericAttributeTraits<T>::CanRepresentValue(isNullable, value))
{
return CHIP_ERROR_INCORRECT_STATE;
}

return encoder.Encode(NumericAttributeTraits<T>::StorageToWorking(value));
}

/// Converts raw ember data from `data` into the encoder
///
/// Uses the attribute `metadata` to determine how the data is encoded into `data` and
/// write a suitable value into `encoder`.
CHIP_ERROR EncodeEmberValue(ByteSpan data, const EmberAfAttributeMetadata * metadata, AttributeValueEncoder & encoder)
{
VerifyOrReturnError(metadata != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

const bool isNullable = metadata->IsNullable();

switch (AttributeBaseType(metadata->attributeType))
{
case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data
return encoder.EncodeNull();
case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean
return EncodeFromSpan<bool>(data, isNullable, encoder);
case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
return EncodeFromSpan<uint8_t>(data, isNullable, encoder);
case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
return EncodeFromSpan<uint16_t>(data, isNullable, encoder);
case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
return EncodeFromSpan<OddSizedInteger<3, false>>(data, isNullable, encoder);
case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
return EncodeFromSpan<uint32_t>(data, isNullable, encoder);
case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
return EncodeFromSpan<OddSizedInteger<5, false>>(data, isNullable, encoder);
case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
return EncodeFromSpan<OddSizedInteger<6, false>>(data, isNullable, encoder);
case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
return EncodeFromSpan<OddSizedInteger<7, false>>(data, isNullable, encoder);
case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
return EncodeFromSpan<uint64_t>(data, isNullable, encoder);
case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
return EncodeFromSpan<int8_t>(data, isNullable, encoder);
case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
return EncodeFromSpan<int16_t>(data, isNullable, encoder);
case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
return EncodeFromSpan<OddSizedInteger<3, true>>(data, isNullable, encoder);
case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
return EncodeFromSpan<int32_t>(data, isNullable, encoder);
case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
return EncodeFromSpan<OddSizedInteger<5, true>>(data, isNullable, encoder);
case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
return EncodeFromSpan<OddSizedInteger<6, true>>(data, isNullable, encoder);
case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
return EncodeFromSpan<OddSizedInteger<7, true>>(data, isNullable, encoder);
case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
return EncodeFromSpan<int64_t>(data, isNullable, encoder);
case ZCL_SINGLE_ATTRIBUTE_TYPE: // 32-bit float
return EncodeFromSpan<float>(data, isNullable, encoder);
case ZCL_DOUBLE_ATTRIBUTE_TYPE: // 64-bit float
return EncodeFromSpan<double>(data, isNullable, encoder);
case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
return EncodeStringLike<CharSpan, ShortPascalString>(data, isNullable, encoder);
case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:
return EncodeStringLike<CharSpan, LongPascalString>(data, isNullable, encoder);
case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
return EncodeStringLike<ByteSpan, ShortPascalString>(data, isNullable, encoder);
case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE:
return EncodeStringLike<ByteSpan, LongPascalString>(data, isNullable, encoder);
default:
ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast<int>(metadata->attributeType));
return CHIP_IM_GLOBAL_STATUS(Failure);
}
}

} // namespace

/// separated-out ReadAttribute implementation (given existing complexity)
Expand Down Expand Up @@ -343,7 +178,11 @@ DataModel::ActionReturnStatus CodegenDataModelProvider::ReadAttribute(const Data
return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status);
}

return EncodeEmberValue(gEmberAttributeIOBufferSpan, attributeMetadata, encoder);
VerifyOrReturnError(attributeMetadata != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

MutableByteSpan data = gEmberAttributeIOBufferSpan;
Ember::EmberAttributeDataBuffer emberData(attributeMetadata, data);
return encoder.Encode(emberData);
}

} // namespace app
Expand Down
Loading

0 comments on commit 692983e

Please sign in to comment.