xref: /aosp_15_r20/external/pigweed/pw_persistent_ram/public/pw_persistent_ram/persistent_buffer.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_bytes/span.h"
22 #include "pw_checksum/crc16_ccitt.h"
23 #include "pw_preprocessor/compiler.h"
24 #include "pw_span/span.h"
25 #include "pw_status/status.h"
26 #include "pw_stream/stream.h"
27 
28 namespace pw::persistent_ram {
29 
30 // A PersistentBufferWriter implements the pw::stream::Writer interface and
31 // provides handles to mutate and access the underlying data of a
32 // PersistentBuffer. This object should NOT be stored in persistent RAM.
33 //
34 // Only one writer should be open at a given time.
35 class PersistentBufferWriter : public stream::NonSeekableWriter {
36  public:
37   PersistentBufferWriter() = delete;
38 
39  private:
40   template <size_t>
41   friend class PersistentBuffer;
42 
PersistentBufferWriter(ByteSpan buffer,volatile size_t & size,volatile uint16_t & checksum)43   PersistentBufferWriter(ByteSpan buffer,
44                          volatile size_t& size,
45                          volatile uint16_t& checksum)
46       : buffer_(buffer), size_(size), checksum_(checksum) {}
47 
48   // Implementation for writing data to this stream.
49   Status DoWrite(ConstByteSpan data) override;
50 
ConservativeLimit(LimitType limit)51   size_t ConservativeLimit(LimitType limit) const override {
52     if (limit == LimitType::kWrite) {
53       return buffer_.size_bytes() - size_;
54     }
55     return 0;
56   }
57 
58   ByteSpan buffer_;
59   volatile size_t& size_;
60   volatile uint16_t& checksum_;
61 };
62 
63 // The PersistentBuffer class intentionally uses uninitialized memory, which
64 // triggers compiler warnings. Disable those warnings for this file.
65 PW_MODIFY_DIAGNOSTICS_PUSH();
66 PW_MODIFY_DIAGNOSTIC(ignored, "-Wuninitialized");
67 PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
68 
69 // When a PersistentBuffer is statically allocated in persistent memory, its
70 // state will persist across soft resets in accordance with the expected
71 // behavior of the underlying RAM. This object is completely safe to use before
72 // static constructors are called as its constructor is effectively a no-op.
73 //
74 // While the stored data can be read by PersistentBuffer's public functions,
75 // each public function must validate the integrity of the stored data. It's
76 // typically more performant to get a handle to a PersistentBufferWriter
77 // instead, as data is validated on creation of the PersistentBufferWriter,
78 // which allows access to the underlying data without needing to validate the
79 // data's integrity with each call to PersistentBufferWriter functions.
80 template <size_t kMaxSizeBytes>
81 class PersistentBuffer {
82  public:
83   // The default constructor intentionally does not initialize anything. This
84   // allows a persistent buffer statically allocated in persistent RAM to be
85   // highly available.
86   //
87   // Explicitly declaring an empty constructor rather than using the default
88   // constructor prevents the object from being zero-initialized when the object
89   // is value initialized. If this was left as a default constructor,
90   // PersistentBuffer objects declared as value-initialized would be
91   // zero-initialized.
92   //
93   //   // Value initialization:
94   //   PersistentBuffer<256> persistent_buffer();
95   //
96   //   // Default initialization:
97   //   PersistentBuffer<256> persistent_buffer;
PersistentBuffer()98   PersistentBuffer() {}
99   // Disable copy and move constructors.
100   PersistentBuffer(const PersistentBuffer&) = delete;
101   PersistentBuffer(PersistentBuffer&&) = delete;
102   // Explicit no-op destructor.
~PersistentBuffer()103   ~PersistentBuffer() {}
104 
GetWriter()105   PersistentBufferWriter GetWriter() {
106     if (!has_value()) {
107       clear();
108     }
109     return PersistentBufferWriter(
110         ByteSpan(const_cast<std::byte*>(buffer_), kMaxSizeBytes),
111         size_,
112         checksum_);
113   }
114 
size()115   size_t size() const {
116     if (has_value()) {
117       return size_;
118     }
119     return 0;
120   }
121 
data()122   const std::byte* data() const { return const_cast<std::byte*>(buffer_); }
123 
clear()124   void clear() {
125     size_ = 0;
126     checksum_ = checksum::Crc16Ccitt::kInitialValue;
127   }
128 
has_value()129   bool has_value() const {
130     if (size_ > kMaxSizeBytes || size_ == 0) {
131       return false;
132     }
133 
134     // Check checksum. This is more costly.
135     return checksum_ == checksum::Crc16Ccitt::Calculate(ConstByteSpan(
136                             const_cast<std::byte*>(buffer_), size_));
137   }
138 
139  private:
140   // None of these members are initialized by the constructor by design.
141   volatile uint16_t checksum_;
142   volatile size_t size_;
143   volatile std::byte buffer_[kMaxSizeBytes];
144 };
145 
146 PW_MODIFY_DIAGNOSTICS_POP();
147 
148 }  // namespace pw::persistent_ram
149