| // Copyright 2021 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <span> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "pw_assert/assert.h" |
| #include "pw_checksum/crc16_ccitt.h" |
| #include "pw_preprocessor/compiler.h" |
| |
| namespace pw::persistent_ram { |
| |
| // Behavior to use when attempting to get a handle to the underlying data stored |
| // in persistent memory. |
| enum class GetterAction { |
| // Default-construct the object before returning a handle. |
| kReset, |
| // Assert that the object is valid before returning a handle. |
| kAssertValid, |
| }; |
| |
| // The Persistent class intentionally uses uninitialized memory, which triggers |
| // compiler warnings. Disable those warnings for this file. |
| PW_MODIFY_DIAGNOSTICS_PUSH(); |
| PW_MODIFY_DIAGNOSTIC(ignored, "-Wuninitialized"); |
| PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized"); |
| |
| // A simple container for holding a value T with CRC16 integrity checking. |
| // |
| // A Persistent is simply a value T plus integrity checking for use in a |
| // persistent RAM section which is not initialized on boot. |
| // |
| // WARNING: Unlike a DoubleBufferedPersistent, a Persistent will be lost if a |
| // write/set operation is interrupted or otherwise not completed. |
| // |
| // TODO(pwbug/348): Consider a different integrity check implementation which |
| // does not use a 512B lookup table. |
| template <typename T> |
| class Persistent { |
| public: |
| // This object provides mutable access to the underlying object of a |
| // Persistent<T>. |
| // |
| // WARNING: This object must remain in scope for any modifications of the |
| // Underlying object. If the object is modified after the Mutator goes out |
| // of scope, the CRC will not be updated to reflect changes, invalidating the |
| // contents of the Persistent<T>! |
| // |
| // WARNING: Persistent<T>::has_value() will return false if there are |
| // in-flight modifications by a Mutator that have not yet been flushed. |
| class Mutator { |
| public: |
| explicit constexpr Mutator(Persistent<T>& persistent) |
| : persistent_(persistent) {} |
| ~Mutator() { persistent_.crc_ = persistent_.CalculateCrc(); } |
| |
| Mutator(const Mutator&) = delete; // Copy constructor is disabled. |
| |
| T* operator->() { return const_cast<T*>(&persistent_.contents_); } |
| |
| // Be careful when sharing a reference or pointer to the underlying object. |
| // Once the Mutator goes out of scope, any changes to the object will |
| // invalidate the checksum. Avoid directly using the underlying object |
| // unless you need to pass it to a function. |
| T& value() { return persistent_.contents_; } |
| T& operator*() { return *const_cast<T*>(&persistent_.contents_); } |
| |
| private: |
| Persistent<T>& persistent_; |
| }; |
| |
| // Constructor which does nothing, meaning it never sets the value. |
| constexpr Persistent() {} |
| |
| Persistent(const Persistent&) = delete; // Copy constructor is disabled. |
| Persistent(Persistent&&) = delete; // Move constructor is disabled. |
| ~Persistent() {} // The destructor does nothing. |
| |
| // Construct the value in-place. |
| template <class... Args> |
| const T& emplace(Args&&... args) { |
| new (const_cast<T*>(&contents_)) T(std::forward<Args>(args)...); |
| crc_ = CalculateCrc(); |
| return const_cast<T&>(contents_); |
| } |
| |
| // Assignment operator. |
| template <typename U = T> |
| Persistent& operator=(U&& value) { |
| contents_ = std::move(value); |
| crc_ = CalculateCrc(); |
| return *this; |
| } |
| |
| // Destroys any contained value. |
| void Invalidate() { |
| // The trivial destructor is skipped as it's trivial. |
| std::memset(const_cast<T*>(&contents_), 0, sizeof(contents_)); |
| crc_ = 0; |
| } |
| |
| // This is deprecated, use Invalidate() instead. |
| [[deprecated]] void reset() { Invalidate(); } |
| |
| // Returns true if a value is held by the Persistent. |
| bool has_value() const { |
| return crc_ == CalculateCrc(); // There's a value if its CRC matches. |
| } |
| |
| // Access the value. |
| // |
| // Precondition: has_value() must be true. |
| const T& value() const { |
| PW_ASSERT(has_value()); |
| return const_cast<T&>(contents_); |
| } |
| |
| // Get a mutable handle to the underlying data. |
| // |
| // Args: |
| // action: Whether to default-construct the underlying value before |
| // providing a mutator, or to assert that the object is valid |
| // without modifying the underlying data. |
| // Precondition: has_value() must be true. |
| Mutator mutator(GetterAction action = GetterAction::kAssertValid) { |
| if (action == GetterAction::kReset) { |
| emplace(); |
| } else { |
| PW_ASSERT(has_value()); |
| } |
| return Mutator(*this); |
| } |
| |
| private: |
| friend class Mutator; |
| |
| static_assert(std::is_trivially_copy_constructible<T>::value, |
| "If a Persistent persists across reboots, it is effectively " |
| "loaded through a trivial copy constructor."); |
| |
| static_assert(std::is_trivially_destructible<T>::value, |
| "A Persistent's destructor does not invoke the value's " |
| "destructor, ergo only trivially destructible types are " |
| "supported."); |
| |
| uint16_t CalculateCrc() const { |
| return checksum::Crc16Ccitt::Calculate( |
| std::as_bytes(std::span(const_cast<const T*>(&contents_), 1))); |
| } |
| |
| // Use unions to denote that these members are never initialized by design and |
| // on purpose. Volatile is used to ensure that the compiler cannot optimize |
| // out operations where it seems like there is no further usage of a |
| // Persistent as this may be on the next boot. |
| union { |
| volatile T contents_; |
| }; |
| union { |
| volatile uint16_t crc_; |
| }; |
| }; |
| |
| PW_MODIFY_DIAGNOSTICS_POP(); |
| |
| } // namespace pw::persistent_ram |