Skip to content

Commit

Permalink
Add support for odd-sized (3, 5, 6, 7 bytes) integer types for attrib…
Browse files Browse the repository at this point in the history
…utes.

Fixes project-chip#8202
  • Loading branch information
bzbarsky-apple committed Nov 16, 2021
1 parent e755a26 commit 56a7970
Show file tree
Hide file tree
Showing 22 changed files with 7,540 additions and 3,920 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"featureLevel": 63,
"featureLevel": 67,
"creator": "zap",
"keyValuePairs": [
{
Expand Down Expand Up @@ -11157,7 +11157,7 @@
"code": 21,
"mfgCode": null,
"side": "server",
"included": 0,
"included": 1,
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
Expand All @@ -11172,7 +11172,7 @@
"code": 22,
"mfgCode": null,
"side": "server",
"included": 0,
"included": 1,
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
Expand Down
46 changes: 44 additions & 2 deletions src/app/util/attribute-storage-null-handling.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#pragma once

#include <lib/core/CHIPTLV.h>
#include <lib/support/TypeTraits.h>

#include <limits>
Expand All @@ -31,6 +32,20 @@ struct NumericAttributeTraits
// store.
using StorageType = T;

// WorkingType is the type used to represent this C++ type when we are
// actually working with it as a value.
using WorkingType = T;

// Convert a working value to a storage value. This uses an outparam
// instead of a return value because some specializations have complicated
// StorageTypes that can't be returned by value. This function can assume
// that WorkingType passed a CanRepresentValue check.
static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue) { storageValue = workingValue; }

// Convert a storage value to a working value. Some specializations do more
// interesting things here.
static constexpr WorkingType StorageToWorking(StorageType storageValue) { return storageValue; }

// The value reserved in the value space of StorageType to represent null,
// for cases when we have a nullable value. This value must match the value
// excluded from the valid value range in the spec, so that we don't confuse
Expand All @@ -40,6 +55,8 @@ struct NumericAttributeTraits

static constexpr bool IsNullValue(StorageType value) { return value == kNullValue; }

static constexpr void SetNull(StorageType & value) { value = kNullValue; }

// Test whether a value can be represented in a "not null" value of the
// given type, which may be a nullable value or not. This needs to be
// implemented for both T and StorageType if the two are distinct.
Expand All @@ -52,20 +69,45 @@ struct NumericAttributeTraits
// it's doing.
return !isNullable || !IsNullValue(value);
}

static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
{
return writer.Put(tag, static_cast<T>(value));
}

// Utility that lets consumers treat a StorageType instance as a uint8_t*
// for writing to the attribute store.
static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
};

template <>
struct NumericAttributeTraits<bool>
{
using StorageType = uint8_t;
static constexpr StorageType kNullValue = 0xFF;
using StorageType = uint8_t;
using WorkingType = bool;

static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue) { storageValue = workingValue; }

static constexpr WorkingType StorageToWorking(StorageType storageValue) { return storageValue; }

static constexpr bool IsNullValue(StorageType value) { return value == kNullValue; }
static constexpr void SetNull(StorageType & value) { value = kNullValue; }
static constexpr bool CanRepresentValue(bool isNullable, StorageType value)
{
// This treats all nonzero values (except the null value) as true.
return !IsNullValue(value);
}
static constexpr bool CanRepresentValue(bool isNullable, bool value) { return true; }

static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
{
return writer.Put(tag, static_cast<bool>(value));
}

static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }

private:
static constexpr StorageType kNullValue = 0xFF;
};

} // namespace app
Expand Down
62 changes: 58 additions & 4 deletions src/app/util/ember-compatibility-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <app/util/attribute-table.h>
#include <app/util/ember-compatibility-functions.h>
#include <app/util/error-mapping.h>
#include <app/util/odd-sized-integers.h>
#include <app/util/util.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLV.h>
Expand Down Expand Up @@ -198,7 +199,7 @@ CHIP_ERROR attributeBufferToNumericTlvData(TLV::TLVWriter & writer, bool isNulla
return CHIP_ERROR_INCORRECT_STATE;
}

return writer.Put(tag, static_cast<T>(value));
return NumericAttributeTraits<T>::Encode(writer, tag, value);
}

} // anonymous namespace
Expand Down Expand Up @@ -283,11 +284,35 @@ CHIP_ERROR ReadSingleClusterData(FabricIndex aAccessingFabricIndex, const Concre
ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint16_t>(*writer, isNullable));
break;
}
case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
{
using IntType = OddSizedInteger<3, false>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
{
ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint32_t>(*writer, isNullable));
break;
}
case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
{
using IntType = OddSizedInteger<5, false>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
{
using IntType = OddSizedInteger<6, false>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
{
using IntType = OddSizedInteger<7, false>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
{
ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint64_t>(*writer, isNullable));
Expand All @@ -303,11 +328,35 @@ CHIP_ERROR ReadSingleClusterData(FabricIndex aAccessingFabricIndex, const Concre
ReturnErrorOnFailure(attributeBufferToNumericTlvData<int16_t>(*writer, isNullable));
break;
}
case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
{
using IntType = OddSizedInteger<3, true>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
{
ReturnErrorOnFailure(attributeBufferToNumericTlvData<int32_t>(*writer, isNullable));
break;
}
case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
{
using IntType = OddSizedInteger<5, true>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
{
using IntType = OddSizedInteger<6, true>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
{
using IntType = OddSizedInteger<7, true>;
ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
break;
}
case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
{
ReturnErrorOnFailure(attributeBufferToNumericTlvData<int64_t>(*writer, isNullable));
Expand Down Expand Up @@ -459,14 +508,14 @@ CHIP_ERROR numericTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isNull
static_assert(sizeof(value) <= sizeof(attributeData), "Value cannot fit into attribute data");
if (isNullable && aReader.GetType() == TLV::kTLVType_Null)
{
value = NumericAttributeTraits<T>::kNullValue;
NumericAttributeTraits<T>::SetNull(value);
}
else
{
T val;
typename NumericAttributeTraits<T>::WorkingType val;
ReturnErrorOnFailure(aReader.Get(val));
VerifyOrReturnError(NumericAttributeTraits<T>::CanRepresentValue(isNullable, val), CHIP_ERROR_INVALID_ARGUMENT);
value = val;
NumericAttributeTraits<T>::WorkingToStorage(val, value);
}
dataLen = sizeof(value);
memcpy(attributeData, &value, sizeof(value));
Expand Down Expand Up @@ -515,6 +564,11 @@ CHIP_ERROR prepareWriteData(const EmberAfAttributeMetadata * metadata, TLV::TLVR
return numericTlvDataToAttributeBuffer<uint8_t>(aReader, isNullable, dataLen);
case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
return numericTlvDataToAttributeBuffer<uint16_t>(aReader, isNullable, dataLen);
case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
{
using IntType = OddSizedInteger<3, false>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
return numericTlvDataToAttributeBuffer<uint32_t>(aReader, isNullable, dataLen);
case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
Expand Down
Loading

0 comments on commit 56a7970

Please sign in to comment.