text: // Copyright 2020 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 <algorithm>
  #include <array>
  #include <cstddef>
  #include <cstring>
  
  #include "pw_containers/vector.h"
  #include "pw_kvs/flash_memory.h"
  #include "pw_span/span.h"
  #include "pw_status/status.h"
  
  namespace pw::kvs {
  
  class FlashError {
   public:
    static constexpr FlashMemory::Address kAnyAddress = FlashMemory::Address(-1);
    static constexpr size_t kAlways = size_t(-1);
  
    // Creates a FlashError that always triggers on the next operation.
    static constexpr FlashError Unconditional(Status status,
                                              size_t times = kAlways,
                                              size_t delay = 0) {
      return FlashError(status, kAnyAddress, 0, times, delay);
    }
  
    // Creates a FlashError that triggers for particular addresses.
    static constexpr FlashError InRange(Status status,
                                        FlashMemory::Address address,
                                        size_t size = 1,
                                        size_t times = kAlways,
                                        size_t delay = 0) {
      return FlashError(status, address, size, times, delay);
    }
  
    // Determines if this FlashError applies to the operation.
    Status Check(FlashMemory::Address start_address, size_t size);
  
    // Determines if any of a series of FlashErrors applies to the operation.
    static Status Check(span<FlashError> errors,
                        FlashMemory::Address address,
                        size_t size);
  
   private:
    constexpr FlashError(Status status,
                         FlashMemory::Address address,
                         size_t size,  // not used if address is kAnyAddress
                         size_t times,
                         size_t delay)
        : status_(status),
          begin_(address),
          end_(address + size),  // not used if address is kAnyAddress
          delay_(delay),
          remaining_(times) {}
  
    const Status status_;
  
    const FlashMemory::Address begin_;
    const FlashMemory::Address end_;  // exclusive
  
    size_t delay_;
    size_t remaining_;
  };
  
  // This uses a buffer to mimic the behaviour of flash (requires erase before
  // write, checks alignments, and is addressed in sectors). The underlying buffer
  // is not initialized.
  class InMemoryFakeFlash : public FlashMemory {
   private:
    static Vector<FlashError, 0> no_errors_;
  
   public:
    // Default to 8-bit alignment.
    static constexpr size_t kDefaultAlignmentBytes = 1;
  
    static constexpr std::byte kErasedValue = std::byte{0xff};
  
    InMemoryFakeFlash(span<std::byte> buffer,
                      size_t sector_size,
                      size_t sector_count,
                      size_t alignment_bytes = kDefaultAlignmentBytes,
                      Vector<FlashError>& read_errors = no_errors_,
                      Vector<FlashError>& write_errors = no_errors_)
        : FlashMemory(sector_size, sector_count, alignment_bytes),
          buffer_(buffer),
          read_errors_(read_errors),
          write_errors_(write_errors) {}
  
    // The fake flash is always enabled.
    Status Enable() override { return Status::OK; }
  
    Status Disable() override { return Status::OK; }
  
    bool IsEnabled() const override { return true; }
  
    // Erase num_sectors starting at a given address.
    Status Erase(Address address, size_t num_sectors) override;
  
    // Reads bytes from flash into buffer.
    StatusWithSize Read(Address address, span<std::byte> output) override;
  
    // Writes bytes to flash.
    StatusWithSize Write(Address address, span<const std::byte> data) override;
  
    // Testing API
  
    // Access the underlying buffer for testing purposes. Not part of the
    // FlashMemory API.
    span<std::byte> buffer() const { return buffer_; }
  
    bool InjectReadError(const FlashError& error) {
      if (read_errors_.full()) {
        return false;
      }
      read_errors_.push_back(error);
      return true;
    }
  
    bool InjectWriteError(const FlashError& error) {
      if (write_errors_.full()) {
        return false;
      }
      write_errors_.push_back(error);
      return true;
    }
  
   private:
    const span<std::byte> buffer_;
    Vector<FlashError>& read_errors_;
    Vector<FlashError>& write_errors_;
  };
  
  // Creates an InMemoryFakeFlash backed by a std::array. The array is initialized
  // to the erased value. A byte array to which to initialize the memory may be
  // provided.
  template <size_t kSectorSize, size_t kSectorCount, size_t kInjectedErrors = 8>
  class FakeFlashBuffer : public InMemoryFakeFlash {
   public:
    // Creates a flash memory with no data written.
    explicit FakeFlashBuffer(size_t alignment_bytes = kDefaultAlignmentBytes)
        : FakeFlashBuffer(std::array<std::byte, 0>{}, alignment_bytes) {}
  
    // Creates a flash memory initialized to the provided contents.
    explicit FakeFlashBuffer(span<const std::byte> contents,
                             size_t alignment_bytes = kDefaultAlignmentBytes)
        : InMemoryFakeFlash(buffer_,
                            kSectorSize,
                            kSectorCount,
                            alignment_bytes,
                            read_errors_,
                            write_errors_) {
      std::memset(buffer_.data(), int(kErasedValue), buffer_.size());
      std::memcpy(buffer_.data(),
                  contents.data(),
                  std::min(contents.size(), buffer_.size()));
    }
  
   private:
    std::array<std::byte, kSectorCount * kSectorSize> buffer_;
    Vector<FlashError, kInjectedErrors> read_errors_;
    Vector<FlashError, kInjectedErrors> write_errors_;
  };
  
  }  // namespace pw::kvs
  