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 24, 2021
1 parent 2e85d48 commit 3096555
Show file tree
Hide file tree
Showing 24 changed files with 8,014 additions and 3,918 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11262,7 +11262,7 @@
"code": 21,
"mfgCode": null,
"side": "server",
"included": 0,
"included": 1,
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
Expand All @@ -11277,7 +11277,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
97 changes: 93 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,16 +564,56 @@ 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_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
{
using IntType = OddSizedInteger<5, false>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
{
using IntType = OddSizedInteger<6, false>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
{
using IntType = OddSizedInteger<7, false>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
return numericTlvDataToAttributeBuffer<uint64_t>(aReader, isNullable, dataLen);
case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
return numericTlvDataToAttributeBuffer<int8_t>(aReader, isNullable, dataLen);
case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
return numericTlvDataToAttributeBuffer<int16_t>(aReader, isNullable, dataLen);
case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
{
using IntType = OddSizedInteger<3, true>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
return numericTlvDataToAttributeBuffer<int32_t>(aReader, isNullable, dataLen);
case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
{
using IntType = OddSizedInteger<5, true>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
{
using IntType = OddSizedInteger<6, true>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
{
using IntType = OddSizedInteger<7, true>;
return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
}
case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
return numericTlvDataToAttributeBuffer<int64_t>(aReader, isNullable, dataLen);
case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
Expand Down
Loading

0 comments on commit 3096555

Please sign in to comment.