-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Expand attribute persistence provider api #27611
Changes from all commits
48915f3
1f989a9
b5ed2d5
c58e9a2
1b1abfe
e398672
6e3d3c7
4630dc1
d60a795
1643373
e1ec87e
4574741
b5d456c
15230fc
476bf9c
76db4d3
333ac54
4f9c481
2d6f26a
2d4cca0
09488ec
582b92c
7c1bf20
81d062f
de3b1c9
35070d1
7f2423f
9ab6af9
e289e60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,12 @@ | |
#pragma once | ||
|
||
#include <app/ConcreteAttributePath.h> | ||
#include <app/data-model/Nullable.h> | ||
#include <app/util/attribute-metadata.h> | ||
#include <cstring> | ||
#include <inttypes.h> | ||
#include <lib/support/BufferReader.h> | ||
#include <lib/support/BufferWriter.h> | ||
#include <lib/support/Span.h> | ||
|
||
namespace chip { | ||
|
@@ -56,17 +61,171 @@ class AttributePersistenceProvider | |
* Read an attribute value from non-volatile memory. | ||
* | ||
* @param [in] aPath the attribute path for the data being persisted. | ||
* @param [in] aMetadata the attribute metadata, as a convenience. | ||
* @param [in] aType the attribute type. | ||
* @param [in] aSize the attribute size. | ||
* @param [in,out] aValue where to place the data. The size of the buffer | ||
* will be equal to `size` member of aMetadata. | ||
* will be equal to `size`. | ||
* | ||
* The data is expected to be in native endianness for | ||
* integers and floats. For strings, see the string | ||
* representation description in the WriteValue | ||
* documentation. | ||
*/ | ||
virtual CHIP_ERROR ReadValue(const ConcreteAttributePath & aPath, const EmberAfAttributeMetadata * aMetadata, | ||
virtual CHIP_ERROR ReadValue(const ConcreteAttributePath & aPath, EmberAfAttributeType aType, size_t aSize, | ||
MutableByteSpan & aValue) = 0; | ||
|
||
/** | ||
* Get the KVS representation of null for the given type. | ||
* @tparam T The type for which the null representation should be returned. | ||
* @return A value of type T that in the KVS represents null. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_same<bool, T>::value, bool> = true> | ||
static uint8_t GetNullValueForNullableType() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this reinventing various machinery from src/app/util/attribute-storage-null-handling.h? |
||
{ | ||
return 0xff; | ||
} | ||
|
||
/** | ||
* Get the KVS representation of null for the given type. | ||
* @tparam T The type for which the null representation should be returned. | ||
* @return A value of type T that in the KVS represents null. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_unsigned<T>::value && !std::is_same<bool, T>::value, bool> = true> | ||
static T GetNullValueForNullableType() | ||
{ | ||
T nullValue = 0; | ||
nullValue = T(~nullValue); | ||
return nullValue; | ||
} | ||
|
||
/** | ||
* Get the KVS representation of null for the given type. | ||
* @tparam T The type for which the null representation should be returned. | ||
* @return A value of type T that in the KVS represents null. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_signed<T>::value && !std::is_same<bool, T>::value, bool> = true> | ||
static T GetNullValueForNullableType() | ||
{ | ||
T shiftBit = 1; | ||
return T(shiftBit << ((sizeof(T) * 8) - 1)); | ||
} | ||
|
||
// The following API provides helper functions to simplify the access of commonly used types. | ||
// The API may not be complete. | ||
// Currently implemented write and read types are: uint8_t, uint16_t, uint32_t, unit64_t and | ||
// their nullable varieties, and bool. | ||
|
||
/** | ||
* Write an attribute value of type intX, uintX or bool to non-volatile memory. | ||
* | ||
* @param [in] aPath the attribute path for the data being written. | ||
* @param [in] aValue the data to write. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this have tested for T being unsigned too, if we are going to assume below that it is? (But watch out for bool....) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know it will; my point is that the actual code in there ends up assuming the value is unsigned, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So concretely. Say this is invoked with T == int8_t, aValue == -1. We will set up a 1-byte buffer. We will then proceed to do |
||
CHIP_ERROR WriteScalarValue(const ConcreteAttributePath & aPath, T & aValue) | ||
{ | ||
uint8_t value[sizeof(T)]; | ||
auto w = Encoding::LittleEndian::BufferWriter(value, sizeof(T)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will completely break on a big-endian system for non-one-byte values, no? The values passed to AttributePersistenceProvider are native-endian for integers! This is very clearly documented above. |
||
w.EndianPut(uint64_t(aValue), sizeof(T)); | ||
|
||
return WriteValue(aPath, ByteSpan(value)); | ||
} | ||
|
||
/** | ||
* Read an attribute of type intX, uintX or bool from non-volatile memory. | ||
* | ||
* @param [in] aPath the attribute path for the data being persisted. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The data is not being persisted.... |
||
* @param [in,out] aValue where to place the data. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | ||
CHIP_ERROR ReadScalarValue(const ConcreteAttributePath & aPath, T & aValue) | ||
{ | ||
uint8_t attrData[sizeof(T)]; | ||
MutableByteSpan tempVal(attrData); | ||
// **Note** aType in the ReadValue function is only used to check if the value is of a string type. Since this template | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, ReadValue is implemented by the application in general. You can't make assumptions about how it will use the data it's handed. |
||
// function is only enabled for integral values, we know that this case will not occur, so we can pass the enum of an | ||
// arbitrary integral type. 0x20 is the ZCL enum type for ZCL_INT8U_ATTRIBUTE_TYPE. | ||
auto err = ReadValue(aPath, 0x20, sizeof(T), tempVal); | ||
if (err != CHIP_NO_ERROR) | ||
{ | ||
return err; | ||
} | ||
|
||
chip::Encoding::LittleEndian::Reader r(tempVal.data(), tempVal.size()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the extra chip::? But again, the data here is native-endian. |
||
r.RawReadLowLevelBeCareful(&aValue); | ||
return r.StatusCode(); | ||
} | ||
|
||
/** | ||
* Write an attribute value of type nullable intX, uintX or bool to non-volatile memory. | ||
* | ||
* @param [in] aPath the attribute path for the data being written. | ||
* @param [in] aValue the data to write. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | ||
CHIP_ERROR WriteScalarValue(const ConcreteAttributePath & aPath, DataModel::Nullable<T> & aValue) | ||
{ | ||
if (aValue.IsNull()) | ||
{ | ||
auto nullVal = GetNullValueForNullableType<T>(); | ||
return WriteScalarValue(aPath, nullVal); | ||
} | ||
return WriteScalarValue(aPath, aValue.Value()); | ||
} | ||
|
||
/** | ||
* Read an attribute of type nullable intX, uintX from non-volatile memory. | ||
* | ||
* @param [in] aPath the attribute path for the data being persisted. | ||
* @param [in,out] aValue where to place the data. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value, bool> = true> | ||
CHIP_ERROR ReadScalarValue(const ConcreteAttributePath & aPath, DataModel::Nullable<T> & aValue) | ||
{ | ||
T tempIntegral; | ||
|
||
CHIP_ERROR err = ReadScalarValue(aPath, tempIntegral); | ||
if (err != CHIP_NO_ERROR) | ||
{ | ||
return err; | ||
} | ||
|
||
if (tempIntegral == GetNullValueForNullableType<T>()) | ||
{ | ||
aValue.SetNull(); | ||
return CHIP_NO_ERROR; | ||
} | ||
|
||
aValue.SetNonNull(tempIntegral); | ||
return CHIP_NO_ERROR; | ||
} | ||
|
||
/** | ||
* Read an attribute of type nullable bool from non-volatile memory. | ||
* | ||
* @param [in] aPath the attribute path for the data being persisted. | ||
* @param [in,out] aValue where to place the data. | ||
*/ | ||
template <typename T, std::enable_if_t<std::is_same<bool, T>::value, bool> = true> | ||
CHIP_ERROR ReadScalarValue(const ConcreteAttributePath & aPath, DataModel::Nullable<T> & aValue) | ||
{ | ||
uint8_t tempIntegral; | ||
|
||
CHIP_ERROR err = ReadScalarValue(aPath, tempIntegral); | ||
if (err != CHIP_NO_ERROR) | ||
{ | ||
return err; | ||
} | ||
|
||
if (tempIntegral == GetNullValueForNullableType<T>()) | ||
{ | ||
aValue.SetNull(); | ||
return CHIP_NO_ERROR; | ||
} | ||
|
||
aValue.SetNonNull(tempIntegral); | ||
return CHIP_NO_ERROR; | ||
} | ||
}; | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an API change to a public SDK API that will break consumers. Why is this ok?