Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use custom ember-buffer decode on codegen data model Read as well. #36229

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading