xref: /aosp_15_r20/external/pigweed/pw_persistent_ram/public/pw_persistent_ram/persistent.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <cstdint>
17 #include <cstring>
18 #include <type_traits>
19 #include <utility>
20 
21 #include "pw_assert/assert.h"
22 #include "pw_checksum/crc16_ccitt.h"
23 #include "pw_preprocessor/compiler.h"
24 #include "pw_span/span.h"
25 
26 namespace pw::persistent_ram {
27 
28 // Behavior to use when attempting to get a handle to the underlying data stored
29 // in persistent memory.
30 enum class GetterAction {
31   // Default-construct the object before returning a handle.
32   kReset,
33   // Assert that the object is valid before returning a handle.
34   kAssertValid,
35 };
36 
37 // The Persistent class intentionally uses uninitialized memory, which triggers
38 // compiler warnings. Disable those warnings for this file.
39 PW_MODIFY_DIAGNOSTICS_PUSH();
40 PW_MODIFY_DIAGNOSTIC(ignored, "-Wuninitialized");
41 PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
42 
43 // A simple container for holding a value T with CRC16 integrity checking.
44 //
45 // A Persistent is simply a value T plus integrity checking for use in a
46 // persistent RAM section which is not initialized on boot.
47 //
48 // WARNING: Unlike a DoubleBufferedPersistent, a Persistent will be lost if a
49 // write/set operation is interrupted or otherwise not completed.
50 //
51 // TODO: b/235277454 - Consider a different integrity check implementation which
52 // does not use a 512B lookup table.
53 template <typename T>
54 class Persistent {
55  public:
56   // This object provides mutable access to the underlying object of a
57   // Persistent<T>.
58   //
59   // WARNING: This object must remain in scope for any modifications of the
60   // Underlying object. If the object is modified after the Mutator goes out
61   // of scope, the CRC will not be updated to reflect changes, invalidating the
62   // contents of the Persistent<T>!
63   //
64   // WARNING: Persistent<T>::has_value() will return false if there are
65   // in-flight modifications by a Mutator that have not yet been flushed.
66   class Mutator {
67    public:
Mutator(Persistent<T> & persistent)68     explicit constexpr Mutator(Persistent<T>& persistent)
69         : persistent_(persistent) {}
~Mutator()70     ~Mutator() { persistent_.crc_ = persistent_.CalculateCrc(); }
71 
72     Mutator(const Mutator&) = delete;  // Copy constructor is disabled.
73 
74     T* operator->() { return const_cast<T*>(&persistent_.contents_); }
75 
76     // Be careful when sharing a reference or pointer to the underlying object.
77     // Once the Mutator goes out of scope, any changes to the object will
78     // invalidate the checksum. Avoid directly using the underlying object
79     // unless you need to pass it to a function.
value()80     T& value() { return persistent_.contents_; }
81     T& operator*() { return *const_cast<T*>(&persistent_.contents_); }
82 
83    private:
84     Persistent<T>& persistent_;
85   };
86 
87   // Constructor which does nothing, meaning it never sets the value.
Persistent()88   constexpr Persistent() {}
89 
90   Persistent(const Persistent&) = delete;  // Copy constructor is disabled.
91   Persistent(Persistent&&) = delete;       // Move constructor is disabled.
~Persistent()92   ~Persistent() {}                         // The destructor does nothing.
93 
94   // Construct the value in-place.
95   template <class... Args>
emplace(Args &&...args)96   const T& emplace(Args&&... args) {
97     new (const_cast<T*>(&contents_)) T(std::forward<Args>(args)...);
98     crc_ = CalculateCrc();
99     return const_cast<T&>(contents_);
100   }
101 
102   // Assignment operator.
103   template <typename U = T>
104   Persistent& operator=(U&& value) {
105     contents_ = std::forward<U>(value);
106     crc_ = CalculateCrc();
107     return *this;
108   }
109 
110   // Destroys any contained value.
Invalidate()111   void Invalidate() {
112     // The trivial destructor is skipped as it's trivial.
113     std::memset(const_cast<T*>(&contents_), 0, sizeof(contents_));
114     crc_ = 0;
115   }
116 
117   // This is deprecated, use Invalidate() instead.
reset()118   [[deprecated]] void reset() { Invalidate(); }
119 
120   // Returns true if a value is held by the Persistent.
has_value()121   bool has_value() const {
122     return crc_ == CalculateCrc();  // There's a value if its CRC matches.
123   }
124 
125   // Access the value.
126   //
127   // Precondition: has_value() must be true.
value()128   const T& value() const {
129     PW_ASSERT(has_value());
130     return const_cast<T&>(contents_);
131   }
132 
133   // Get a mutable handle to the underlying data.
134   //
135   // Args:
136   //   action: Whether to default-construct the underlying value before
137   //           providing a mutator, or to assert that the object is valid
138   //           without modifying the underlying data.
139   // Precondition: has_value() must be true.
140   Mutator mutator(GetterAction action = GetterAction::kAssertValid) {
141     if (action == GetterAction::kReset) {
142       emplace();
143     } else {
144       PW_ASSERT(has_value());
145     }
146     return Mutator(*this);
147   }
148 
149  private:
150   friend class Mutator;
151 
152   static_assert(std::is_trivially_copy_constructible<T>::value,
153                 "If a Persistent persists across reboots, it is effectively "
154                 "loaded through a trivial copy constructor.");
155 
156   static_assert(std::is_trivially_destructible<T>::value,
157                 "A Persistent's destructor does not invoke the value's "
158                 "destructor, ergo only trivially destructible types are "
159                 "supported.");
160 
CalculateCrc()161   uint16_t CalculateCrc() const {
162     return checksum::Crc16Ccitt::Calculate(
163         as_bytes(span(const_cast<const T*>(&contents_), 1)));
164   }
165 
166   // Use unions to denote that these members are never initialized by design and
167   // on purpose. Volatile is used to ensure that the compiler cannot optimize
168   // out operations where it seems like there is no further usage of a
169   // Persistent as this may be on the next boot.
170   union {
171     volatile T contents_;
172   };
173   union {
174     volatile uint16_t crc_;
175   };
176 };
177 
178 PW_MODIFY_DIAGNOSTICS_POP();
179 
180 }  // namespace pw::persistent_ram
181