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