Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : *
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 : #pragma once
17 :
18 : #include <app/AttributeValueDecoder.h>
19 : #include <app/ConcreteAttributePath.h>
20 : #include <app/data-model-provider/ActionReturnStatus.h>
21 : #include <app/persistence/AttributePersistenceProvider.h>
22 : #include <app/persistence/String.h>
23 :
24 : #include <type_traits>
25 :
26 : namespace chip::app {
27 :
28 : /// Provides functionality for handling attribute persistence via
29 : /// an AttributePersistenceProvider.
30 : ///
31 : /// AttributePersistenceProvider works with raw bytes, however attributes
32 : /// have known (strong) types and their load/decode logic is often
33 : /// similar and reusable. This class implements the logic of handling
34 : /// such attributes, so that it can be reused across cluster implementations.
35 : class AttributePersistence
36 : {
37 : public:
38 63 : AttributePersistence(AttributePersistenceProvider & provider) : mProvider(provider) {}
39 :
40 : /// Loads a native-endianness stored value of type `T` into `value` from the persistence provider.
41 : ///
42 : /// If load fails, `false` is returned and data is filled with `valueOnLoadFailure`.
43 : ///
44 : /// Error reason for load failure is logged (or nothing logged in case "Value not found" is the
45 : /// reason for the load failure).
46 : template <typename T, typename std::enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>> * = nullptr>
47 50 : bool LoadNativeEndianValue(const ConcreteAttributePath & path, T & value, const T & valueOnLoadFailure)
48 : {
49 50 : return InternalRawLoadNativeEndianValue(path, &value, &valueOnLoadFailure, sizeof(T));
50 : }
51 :
52 : /// Nullable
53 : /// Loads a native-endianness stored value of type `T` into `value` from the persistence provider.
54 : ///
55 : /// If load fails, `false` is returned and data is filled with `valueOnLoadFailure`.
56 : ///
57 : /// Error reason for load failure is logged (or nothing logged in case "Value not found" is the
58 : /// reason for the load failure).
59 : template <typename T, typename std::enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>> * = nullptr>
60 15 : bool LoadNativeEndianValue(const ConcreteAttributePath & path, DataModel::Nullable<T> & value,
61 : const DataModel::Nullable<T> & valueOnLoadFailure)
62 : {
63 : typename NumericAttributeTraits<T>::StorageType storageReadValue;
64 : typename NumericAttributeTraits<T>::StorageType storageDefaultValue;
65 :
66 15 : NullableToStorage(valueOnLoadFailure, storageDefaultValue);
67 15 : bool success = InternalRawLoadNativeEndianValue(path, &storageReadValue, &storageDefaultValue, sizeof(T));
68 15 : StorageToNullable(storageReadValue, value);
69 :
70 15 : return success;
71 : }
72 :
73 : /// Performs all the steps of:
74 : /// - decode the given raw data
75 : /// - validate that the decoded value is different from the current one
76 : /// - write to storage
77 : template <typename T, typename std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
78 5 : DataModel::ActionReturnStatus DecodeAndStoreNativeEndianValue(const ConcreteAttributePath & path,
79 : AttributeValueDecoder & decoder, T & value)
80 : {
81 5 : T decodedValue{};
82 5 : ReturnErrorOnFailure(decoder.Decode(decodedValue));
83 5 : VerifyOrReturnValue(decodedValue != value, DataModel::ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
84 4 : value = decodedValue;
85 4 : return mProvider.WriteValue(path, { reinterpret_cast<const uint8_t *>(&value), sizeof(value) });
86 : }
87 :
88 : /// Nullable type handling
89 : /// Performs all the steps of:
90 : /// - decode the given raw data
91 : /// - validate that the decoded value is different from the current one
92 : /// - write to storage
93 : template <typename T, typename std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
94 10 : DataModel::ActionReturnStatus DecodeAndStoreNativeEndianValue(const ConcreteAttributePath & path,
95 : AttributeValueDecoder & decoder, DataModel::Nullable<T> & value)
96 : {
97 10 : DataModel::Nullable<T> decodedValue{};
98 10 : ReturnErrorOnFailure(decoder.Decode(decodedValue));
99 10 : VerifyOrReturnValue(decodedValue != value, DataModel::ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
100 8 : value = decodedValue;
101 :
102 : typename NumericAttributeTraits<T>::StorageType storageValue;
103 8 : NullableToStorage(value, storageValue);
104 :
105 8 : return mProvider.WriteValue(path, { reinterpret_cast<const uint8_t *>(&storageValue), sizeof(storageValue) });
106 : }
107 :
108 : // Specialization for enums
109 : // - decode the given data
110 : // - verifies that it is a valid enum value
111 : // - validate that the decoded value is different from the current one
112 : // - writes to storage
113 : template <typename T, typename std::enable_if_t<std::is_enum_v<T>> * = nullptr>
114 6 : DataModel::ActionReturnStatus DecodeAndStoreNativeEndianValue(const ConcreteAttributePath & path,
115 : AttributeValueDecoder & decoder, T & value)
116 : {
117 6 : T decodedValue = T::kUnknownEnumValue;
118 6 : ReturnErrorOnFailure(decoder.Decode(decodedValue));
119 6 : VerifyOrReturnError(decodedValue != T::kUnknownEnumValue, CHIP_IM_GLOBAL_STATUS(ConstraintError));
120 5 : VerifyOrReturnValue(decodedValue != value, DataModel::ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
121 4 : value = decodedValue;
122 4 : return mProvider.WriteValue(path, { reinterpret_cast<const uint8_t *>(&value), sizeof(value) });
123 : }
124 :
125 : // Nullable
126 : // Specialization for enums
127 : // - decode the given data
128 : // - verifies that it is a valid enum value
129 : // - validate that the decoded value is different from the current one
130 : // - writes to storage
131 : template <typename T, typename std::enable_if_t<std::is_enum_v<T>> * = nullptr>
132 12 : DataModel::ActionReturnStatus DecodeAndStoreNativeEndianValue(const ConcreteAttributePath & path,
133 : AttributeValueDecoder & decoder, DataModel::Nullable<T> & value)
134 : {
135 12 : DataModel::Nullable<T> decodedValue{};
136 12 : ReturnErrorOnFailure(decoder.Decode(decodedValue));
137 12 : VerifyOrReturnError(decodedValue.IsNull() || decodedValue.Value() != T::kUnknownEnumValue,
138 : CHIP_IM_GLOBAL_STATUS(ConstraintError));
139 11 : VerifyOrReturnValue(decodedValue != value, DataModel::ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
140 9 : value = decodedValue;
141 :
142 : typename NumericAttributeTraits<T>::StorageType storageValue;
143 9 : NullableToStorage(value, storageValue);
144 :
145 9 : return mProvider.WriteValue(path, { reinterpret_cast<const uint8_t *>(&storageValue), sizeof(storageValue) });
146 : }
147 :
148 : /// Load the given string from concrete storage.
149 : ///
150 : /// NOTE: `value` is take as an internal short string to avoid the templates that Storage::String
151 : /// implies, however callers are generally expected to pass in a `Storage::String` value and
152 : /// not use internal classes directly.
153 : ///
154 : /// Returns true on success, false on failure. On failure the string is reset to empty.
155 : bool LoadString(const ConcreteAttributePath & path, Storage::Internal::ShortString & value);
156 :
157 : /// Store the given string in persistent storage.
158 : ///
159 : /// NOTE: `value` is take as an internal short string to avoid the templates that Storage::String
160 : /// implies, however callers are generally expected to pass in a `Storage::String` value and
161 : /// not use internal classes directly.
162 : CHIP_ERROR StoreString(const ConcreteAttributePath & path, const Storage::Internal::ShortString & value);
163 :
164 : private:
165 : AttributePersistenceProvider & mProvider;
166 :
167 : /// Loads a raw value of size `size` into the memory pointed to by `data`.
168 : /// If load fails, `false` is returned and data is filled with `valueOnLoadFailure`.
169 : ///
170 : /// Error reason for load failure is logged (or nothing logged in case "Value not found" is the
171 : /// reason for the load failure).
172 : bool InternalRawLoadNativeEndianValue(const ConcreteAttributePath & path, void * data, const void * valueOnLoadFailure,
173 : size_t size);
174 : };
175 :
176 : } // namespace chip::app
|