blob: ab2e41eeb2d910884a5a7102a3a7bdf4eca2782b [file] [log] [blame]
// 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
// 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 <type_traits>
#include <utility>
#include "pw_bytes/span.h"
#include "pw_checksum/crc16_ccitt.h"
#include "pw_preprocessor/compiler.h"
#include "pw_span/span.h"
#include "pw_status/status.h"
#include "pw_stream/stream.h"
namespace pw::persistent_ram {
// A PersistentBufferWriter implements the pw::stream::Writer interface and
// provides handles to mutate and access the underlying data of a
// PersistentBuffer. This object should NOT be stored in persistent RAM.
// Only one writer should be open at a given time.
class PersistentBufferWriter : public stream::NonSeekableWriter {
PersistentBufferWriter() = delete;
template <size_t>
friend class PersistentBuffer;
PersistentBufferWriter(ByteSpan buffer,
volatile size_t& size,
volatile uint16_t& checksum)
: buffer_(buffer), size_(size), checksum_(checksum) {}
// Implementation for writing data to this stream.
Status DoWrite(ConstByteSpan data) override;
size_t ConservativeLimit(LimitType limit) const override {
if (limit == LimitType::kWrite) {
return buffer_.size_bytes() - size_;
return 0;
ByteSpan buffer_;
volatile size_t& size_;
volatile uint16_t& checksum_;
// The PersistentBuffer class intentionally uses uninitialized memory, which
// triggers compiler warnings. Disable those warnings for this file.
PW_MODIFY_DIAGNOSTIC(ignored, "-Wuninitialized");
PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
// When a PersistentBuffer is statically allocated in persistent memory, its
// state will persist across soft resets in accordance with the expected
// behavior of the underlying RAM. This object is completely safe to use before
// static constructors are called as its constructor is effectively a no-op.
// While the stored data can be read by PersistentBuffer's public functions,
// each public function must validate the integrity of the stored data. It's
// typically more performant to get a handle to a PersistentBufferWriter
// instead, as data is validated on creation of the PersistentBufferWriter,
// which allows access to the underlying data without needing to validate the
// data's integrity with each call to PersistentBufferWriter functions.
template <size_t kMaxSizeBytes>
class PersistentBuffer {
// The default constructor intentionally does not initialize anything. This
// allows a persistent buffer statically allocated in persistent RAM to be
// highly available.
// Explicitly declaring an empty constructor rather than using the default
// constructor prevents the object from being zero-initialized when the object
// is value initialized. If this was left as a default constructor,
// PersistentBuffer objects declared as value-initialized would be
// zero-initialized.
// // Value initialization:
// PersistentBuffer<256> persistent_buffer();
// // Default initialization:
// PersistentBuffer<256> persistent_buffer;
PersistentBuffer() {}
// Disable copy and move constructors.
PersistentBuffer(const PersistentBuffer&) = delete;
PersistentBuffer(PersistentBuffer&&) = delete;
// Explicit no-op destructor.
~PersistentBuffer() {}
PersistentBufferWriter GetWriter() {
if (!has_value()) {
return PersistentBufferWriter(
ByteSpan(const_cast<std::byte*>(buffer_), kMaxSizeBytes),
size_t size() const {
if (has_value()) {
return size_;
return 0;
const std::byte* data() const { return const_cast<std::byte*>(buffer_); }
void clear() {
size_ = 0;
checksum_ = checksum::Crc16Ccitt::kInitialValue;
bool has_value() const {
if (size_ > kMaxSizeBytes || size_ == 0) {
return false;
// Check checksum. This is more costly.
return checksum_ == checksum::Crc16Ccitt::Calculate(ConstByteSpan(
const_cast<std::byte*>(buffer_), size_));
// None of these members are initialized by the constructor by design.
volatile uint16_t checksum_;
volatile size_t size_;
volatile std::byte buffer_[kMaxSizeBytes];
} // namespace pw::persistent_ram