mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-16 20:59:51 +03:00
move some vectors to psram, fix syslog start/stop
This commit is contained in:
29
lib/esp32-psram/src/esp32-psram.h
Normal file
29
lib/esp32-psram/src/esp32-psram.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP32
|
||||
/**
|
||||
* @file ESP32-PSRAM.h
|
||||
* @brief Main include file for the ESP32-PSRAM library
|
||||
*
|
||||
* This header includes all components of the ESP32-PSRAM library and
|
||||
* provides a using namespace directive for easier access to library features.
|
||||
*/
|
||||
|
||||
// Include all library components
|
||||
#include "esp32-psram/AllocatorPSRAM.h" // PSRAM-backed vector
|
||||
#include "esp32-psram/VectorPSRAM.h" // PSRAM-backed vector
|
||||
#include "esp32-psram/VectorHIMEM.h" // HIMEM-backed vector
|
||||
// #include "esp32-psram/InMemoryFile.h" // File interface using vectors
|
||||
// #include "esp32-psram/PSRAM.h" // PSRAM file system
|
||||
// #include "esp32-psram/HIMEM.h" // HIMEM file system
|
||||
#include "esp32-psram/RingBufferStream.h" // Stream-based ring buffer
|
||||
#include "esp32-psram/TypedRingBuffer.h" // Typed ring buffer for structured data
|
||||
|
||||
#ifndef ESP32_PSRAM_NO_NAMESPACE
|
||||
using namespace esp32_psram;
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "This library is only compatible with ESP32 platforms."
|
||||
#endif // ESP32
|
||||
|
||||
159
lib/esp32-psram/src/esp32-psram/AllocatorPSRAM.h
Normal file
159
lib/esp32-psram/src/esp32-psram/AllocatorPSRAM.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
/**
|
||||
* @namespace esp32_psram
|
||||
* @brief Namespace containing ESP32 PSRAM-specific implementations
|
||||
*/
|
||||
namespace esp32_psram {
|
||||
|
||||
/**
|
||||
* @class AllocatorPSRAM
|
||||
* @brief Custom allocator that uses ESP32's PSRAM for memory allocation
|
||||
* @tparam T Type of elements to allocate
|
||||
*
|
||||
* This allocator uses ESP32's heap_caps_malloc with MALLOC_CAP_SPIRAM flag
|
||||
* to ensure all memory is allocated in PSRAM instead of regular RAM. If PSRAM
|
||||
* allocation fails, it falls back to regular RAM.
|
||||
*/
|
||||
template <typename T>
|
||||
class AllocatorPSRAM {
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
AllocatorPSRAM() noexcept {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor from another allocator type
|
||||
* @tparam U Type of the other allocator
|
||||
* @param other The other allocator
|
||||
*/
|
||||
template <typename U>
|
||||
AllocatorPSRAM(const AllocatorPSRAM<U>&) noexcept {}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory from PSRAM
|
||||
* @param n Number of elements to allocate
|
||||
* @return Pointer to allocated memory
|
||||
* @throws std::bad_alloc If allocation fails or size is too large
|
||||
*/
|
||||
pointer allocate(size_type n) {
|
||||
// if (n > std::numeric_limits<size_type>::max() / sizeof(T))
|
||||
// throw std::bad_alloc();
|
||||
// in Arduino excepitons are disabled!
|
||||
assert(n <= std::numeric_limits<size_type>::max() / sizeof(T));
|
||||
|
||||
pointer p = static_cast<pointer>(
|
||||
heap_caps_malloc(n * sizeof(T), MALLOC_CAP_SPIRAM));
|
||||
if (p == nullptr) {
|
||||
p = static_cast<pointer>(malloc(n * sizeof(T)));
|
||||
}
|
||||
|
||||
// if (!p) throw std::bad_alloc();
|
||||
|
||||
// in Arduino excepitons are disabled!
|
||||
assert(p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deallocate memory
|
||||
* @param p Pointer to memory to deallocate
|
||||
* @param size Size of allocation (unused)
|
||||
*/
|
||||
void deallocate(pointer p, size_type) noexcept { heap_caps_free(p); }
|
||||
|
||||
/**
|
||||
* @brief Rebind allocator to another type
|
||||
* @tparam U Type to rebind the allocator to
|
||||
*/
|
||||
template <typename U>
|
||||
struct rebind {
|
||||
using other = AllocatorPSRAM<U>;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class AllocatorOnlyPSRAM
|
||||
* @brief Custom allocator that uses ESP32's PSRAM for memory allocation
|
||||
* @tparam T Type of elements to allocate
|
||||
*
|
||||
* This allocator uses ESP32's heap_caps_malloc with MALLOC_CAP_SPIRAM flag
|
||||
* to ensure all memory is allocated in PSRAM instead of regular RAM.
|
||||
* If PSRAM allocation fails, it does not fall back to regular RAM.
|
||||
*/
|
||||
template <typename T>
|
||||
class AllocatorOnlyPSRAM {
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
AllocatorOnlyPSRAM() noexcept {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor from another allocator type
|
||||
* @tparam U Type of the other allocator
|
||||
* @param other The other allocator
|
||||
*/
|
||||
template <typename U>
|
||||
AllocatorOnlyPSRAM(const AllocatorOnlyPSRAM<U>&) noexcept {}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory from PSRAM
|
||||
* @param n Number of elements to allocate
|
||||
* @return Pointer to allocated memory
|
||||
* @throws std::bad_alloc If allocation fails or size is too large
|
||||
*/
|
||||
pointer allocate(size_type n) {
|
||||
// if (n > std::numeric_limits<size_type>::max() / sizeof(T))
|
||||
// throw std::bad_alloc();
|
||||
// in Arduino excepitons are disabled!
|
||||
assert(n <= std::numeric_limits<size_type>::max() / sizeof(T));
|
||||
|
||||
pointer p = static_cast<pointer>(
|
||||
heap_caps_malloc(n * sizeof(T), MALLOC_CAP_SPIRAM));
|
||||
// if (!p) throw std::bad_alloc();
|
||||
|
||||
// in Arduino excepitons are disabled!
|
||||
assert(p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deallocate memory
|
||||
* @param p Pointer to memory to deallocate
|
||||
* @param size Size of allocation (unused)
|
||||
*/
|
||||
void deallocate(pointer p, size_type) noexcept { heap_caps_free(p); }
|
||||
|
||||
/**
|
||||
* @brief Rebind allocator to another type
|
||||
* @tparam U Type to rebind the allocator to
|
||||
*/
|
||||
template <typename U>
|
||||
struct rebind {
|
||||
using other = AllocatorOnlyPSRAM<U>;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace esp32_psram
|
||||
54
lib/esp32-psram/src/esp32-psram/HIMEM.h
Normal file
54
lib/esp32-psram/src/esp32-psram/HIMEM.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "InMemoryFS.h"
|
||||
#include "VectorHIMEM.h"
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
/**
|
||||
* @class HIMEMClass
|
||||
* @brief Class for managing files stored in ESP32's High Memory (HIMEM)
|
||||
*
|
||||
* This class provides an interface similar to SD.h for managing files
|
||||
* that are stored in HIMEM memory (beyond the 4MB boundary) rather than on an SD card.
|
||||
* HIMEM offers larger storage capacity but slightly slower access than regular PSRAM.
|
||||
*/
|
||||
class HIMEMClass : public InMemoryFS<VectorHIMEM<uint8_t>, FileHIMEM> {
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize the HIMEM filesystem
|
||||
* @return true if initialization was successful, false otherwise
|
||||
*/
|
||||
bool begin() override {
|
||||
if (esp_himem_get_free_size() > 0) {
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
initialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get total space (returns available HIMEM)
|
||||
* @return Total HIMEM size in bytes
|
||||
*/
|
||||
uint64_t totalBytes() override {
|
||||
return esp_himem_get_phys_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get free space (returns free HIMEM)
|
||||
* @return Free HIMEM size in bytes
|
||||
*/
|
||||
uint64_t freeBytes() override {
|
||||
return esp_himem_get_free_size();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Global instance of HIMEMClass for easy access
|
||||
*/
|
||||
static HIMEMClass HIMEM;
|
||||
|
||||
} // namespace esp32_psram
|
||||
362
lib/esp32-psram/src/esp32-psram/HimemBlock.h
Normal file
362
lib/esp32-psram/src/esp32-psram/HimemBlock.h
Normal file
@@ -0,0 +1,362 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
// ESP32 HIMEM headers - using conditional inclusion for compatibility
|
||||
#if __has_include("esp32/himem.h")
|
||||
#include "esp32/himem.h"
|
||||
#elif __has_include("esp_himem.h")
|
||||
#include "esp_himem.h"
|
||||
#else
|
||||
// Fall back to Arduino ESP32 core path
|
||||
extern "C" {
|
||||
#include "esp32/himem.h"
|
||||
}
|
||||
|
||||
// Define missing constants if needed
|
||||
#ifndef ESP_HIMEM_BLKSZ
|
||||
#define ESP_HIMEM_BLKSZ (32 * 1024)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Make sure ESP_HIMEM_PROT_RW is defined
|
||||
#ifndef ESP_HIMEM_PROT_RW
|
||||
#define ESP_HIMEM_PROT_RW 0
|
||||
#endif
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
static constexpr const char* TAG = "HIMEM"; // Tag for ESP logging
|
||||
|
||||
/**
|
||||
* @class HimemBlock
|
||||
* @brief Manages a block of himem memory with mapping functionality
|
||||
*/
|
||||
class HimemBlock {
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
HimemBlock() { ESP_LOGD(TAG, "HimemBlock constructor called"); }
|
||||
|
||||
/**
|
||||
* @brief Allocate a block of himem
|
||||
* @param block_size Size of memory to allocate in bytes
|
||||
* @return the allocated (block) size in bytes, 0 otherwise
|
||||
*/
|
||||
size_t allocate(size_t block_size) {
|
||||
ESP_LOGI(TAG, "HimemBlock::allocate(%u bytes) - Current handle: %p",
|
||||
block_size, handle);
|
||||
|
||||
if (handle != 0) {
|
||||
ESP_LOGW(TAG, "Cannot allocate: Block already allocated");
|
||||
return false; // Already allocated
|
||||
}
|
||||
|
||||
// Round up to the nearest multiple of ESP_HIMEM_BLKSZ (32K)
|
||||
block_size = ((block_size + ESP_HIMEM_BLKSZ - 1) / ESP_HIMEM_BLKSZ) *
|
||||
ESP_HIMEM_BLKSZ;
|
||||
|
||||
size = block_size;
|
||||
esp_err_t err = esp_himem_alloc(block_size, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "HIMEM allocation failed - error: %d, size: %d", err,
|
||||
block_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "- Successfully allocated %u bytes, handle: %p", block_size,
|
||||
handle);
|
||||
return block_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from HIMEM at specified offset
|
||||
* @param dest Destination buffer
|
||||
* @param offset Offset in HIMEM to read from
|
||||
* @param length Number of bytes to read
|
||||
* @return Number of bytes actually read
|
||||
*/
|
||||
size_t read(void* dest, size_t offset, size_t length) {
|
||||
ESP_LOGD(TAG, "HimemBlock::read(dst=%p, offset=%u, length=%u)", dest,
|
||||
offset, length);
|
||||
|
||||
if (!handle || offset >= size) {
|
||||
ESP_LOGW(TAG, "Read failed: %s",
|
||||
!handle ? "Invalid handle" : "Offset beyond size");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure we don't read past the end
|
||||
length = std::min(length, size - offset);
|
||||
if (length == 0) {
|
||||
ESP_LOGW(TAG, "Read failed: Zero length after bounds check");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate which block this belongs to
|
||||
size_t block_index = offset / ESP_HIMEM_BLKSZ;
|
||||
size_t block_offset = offset % ESP_HIMEM_BLKSZ;
|
||||
size_t bytes_read = 0;
|
||||
|
||||
ESP_LOGD(TAG, "- Reading from block %u, offset %u", block_index,
|
||||
block_offset);
|
||||
uint8_t* dest_ptr = static_cast<uint8_t*>(dest);
|
||||
|
||||
while (bytes_read < length) {
|
||||
// Ensure the correct block is mapped
|
||||
if (!ensure_block_mapped(block_index)) {
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
// Calculate how much to read from this block
|
||||
size_t block_remain = ESP_HIMEM_BLKSZ - block_offset;
|
||||
size_t to_read = std::min(block_remain, length - bytes_read);
|
||||
ESP_LOGD(TAG, "- Reading %u bytes from window at offset %u", to_read,
|
||||
block_offset);
|
||||
|
||||
// Copy the data
|
||||
memcpy(dest_ptr + bytes_read,
|
||||
static_cast<uint8_t*>(mapped_ptr) + block_offset, to_read);
|
||||
|
||||
bytes_read += to_read;
|
||||
block_index++;
|
||||
block_offset = 0; // Reset offset for next blocks
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "- Successfully read %u bytes", bytes_read);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write data to HIMEM at specified offset
|
||||
* @param src Source buffer
|
||||
* @param offset Offset in HIMEM to write to
|
||||
* @param length Number of bytes to write
|
||||
* @return Number of bytes actually written
|
||||
*/
|
||||
size_t write(const void* src, size_t offset, size_t length) {
|
||||
ESP_LOGD(TAG, "HimemBlock::write(src=%p, offset=%u, length=%u)", src,
|
||||
offset, length);
|
||||
|
||||
if (!handle || offset >= size) {
|
||||
ESP_LOGW(TAG, "Write failed: %s",
|
||||
!handle ? "Invalid handle" : "Offset beyond size");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure we don't write past the end
|
||||
length = std::min(length, size - offset);
|
||||
if (length == 0) {
|
||||
ESP_LOGW(TAG, "Write failed: Zero length after bounds check");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate which block this belongs to
|
||||
size_t block_index = offset / ESP_HIMEM_BLKSZ;
|
||||
size_t block_offset = offset % ESP_HIMEM_BLKSZ;
|
||||
size_t bytes_written = 0;
|
||||
|
||||
ESP_LOGD(TAG, "- Writing to block %u, offset %u", block_index,
|
||||
block_offset);
|
||||
const uint8_t* src_ptr = static_cast<const uint8_t*>(src);
|
||||
|
||||
while (bytes_written < length) {
|
||||
// Ensure the correct block is mapped
|
||||
if (!ensure_block_mapped(block_index)) {
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
// Calculate how much to write to this block
|
||||
size_t block_remain = ESP_HIMEM_BLKSZ - block_offset;
|
||||
size_t to_write = std::min(block_remain, length - bytes_written);
|
||||
ESP_LOGD(TAG, "- Writing %u bytes to window at offset %u", to_write,
|
||||
block_offset);
|
||||
|
||||
// Copy the data
|
||||
memcpy(static_cast<uint8_t*>(mapped_ptr) + block_offset,
|
||||
src_ptr + bytes_written, to_write);
|
||||
|
||||
bytes_written += to_write;
|
||||
block_index++;
|
||||
block_offset = 0; // Reset offset for next blocks
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "- Successfully wrote %u bytes", bytes_written);
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
bool getAddress(size_t offset, void* &result, size_t &available) {
|
||||
size_t block_index = offset / ESP_HIMEM_BLKSZ;
|
||||
size_t block_offset = offset % ESP_HIMEM_BLKSZ;
|
||||
if (!ensure_block_mapped(block_index)) return false;
|
||||
available = ESP_HIMEM_BLKSZ - block_offset;
|
||||
result = static_cast<uint8_t*>(mapped_ptr)+block_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unmap the himem block
|
||||
*/
|
||||
void unmap() {
|
||||
ESP_LOGD(TAG, "HimemBlock::unmap() - mapped_ptr=%p, range=%p", mapped_ptr,
|
||||
range);
|
||||
|
||||
if (mapped_ptr && range) {
|
||||
ESP_LOGD(TAG, "- Unmapping memory and freeing range");
|
||||
esp_himem_unmap(range, mapped_ptr, ESP_HIMEM_BLKSZ);
|
||||
esp_himem_free_map_range(range);
|
||||
mapped_ptr = nullptr;
|
||||
range = 0;
|
||||
current_mapped_block = SIZE_MAX; // Reset currently mapped block
|
||||
ESP_LOGD(TAG, "- Unmapped successfully");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "- Nothing to unmap");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free the himem block
|
||||
*/
|
||||
void free() {
|
||||
ESP_LOGD(TAG, "HimemBlock::free() - handle=%p", handle);
|
||||
|
||||
if (handle) {
|
||||
ESP_LOGD(TAG, "- Unmapping before freeing");
|
||||
unmap();
|
||||
ESP_LOGD(TAG, "- Freeing HIMEM handle %p", handle);
|
||||
esp_himem_free(handle);
|
||||
handle = 0;
|
||||
size = 0;
|
||||
ESP_LOGD(TAG, " - Successfully freed HIMEM block");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "- Nothing to free");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the size of the allocated block
|
||||
* @return Size of the allocated block in bytes
|
||||
*/
|
||||
size_t get_size() const {
|
||||
ESP_LOGD(TAG, "HimemBlock::get_size() = %u", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor - ensures memory is properly freed
|
||||
*/
|
||||
~HimemBlock() {
|
||||
ESP_LOGD(TAG, "HimemBlock destructor called");
|
||||
free();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor (deleted)
|
||||
*/
|
||||
HimemBlock(const HimemBlock&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
HimemBlock(HimemBlock&& other) noexcept
|
||||
: handle(other.handle),
|
||||
range(other.range),
|
||||
mapped_ptr(other.mapped_ptr),
|
||||
size(other.size),
|
||||
current_mapped_block(other.current_mapped_block) {
|
||||
ESP_LOGD(TAG, "HimemBlock move constructor - moving handle=%p, size=%u",
|
||||
other.handle, other.size);
|
||||
other.handle = 0;
|
||||
other.range = 0;
|
||||
other.mapped_ptr = nullptr;
|
||||
other.size = 0;
|
||||
other.current_mapped_block = SIZE_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator (deleted)
|
||||
*/
|
||||
HimemBlock& operator=(const HimemBlock&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator
|
||||
*/
|
||||
HimemBlock& operator=(HimemBlock&& other) noexcept {
|
||||
ESP_LOGD(TAG, "HimemBlock move assignment - from handle=%p to handle=%p",
|
||||
other.handle, handle);
|
||||
|
||||
if (this != &other) {
|
||||
ESP_LOGD(TAG, "Freeing current resources before move assignment");
|
||||
free();
|
||||
handle = other.handle;
|
||||
range = other.range;
|
||||
mapped_ptr = other.mapped_ptr;
|
||||
size = other.size;
|
||||
current_mapped_block = other.current_mapped_block;
|
||||
ESP_LOGD(TAG, "Moved resources, new size=%u", size);
|
||||
other.handle = 0;
|
||||
other.range = 0;
|
||||
other.mapped_ptr = nullptr;
|
||||
other.size = 0;
|
||||
other.current_mapped_block = SIZE_MAX;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Self-assignment detected, no action taken");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
esp_himem_handle_t handle = 0;
|
||||
esp_himem_rangehandle_t range = 0;
|
||||
void* mapped_ptr = nullptr;
|
||||
size_t size = 0;
|
||||
size_t current_mapped_block =
|
||||
SIZE_MAX; // Track which block is currently mapped
|
||||
|
||||
/**
|
||||
* @brief Ensure a specific block is mapped into memory
|
||||
* @param block_index The index of the block to map
|
||||
* @return true if mapping successful, false otherwise
|
||||
*/
|
||||
bool ensure_block_mapped(size_t block_index) {
|
||||
// If the requested block is already mapped, we're done
|
||||
if (block_index == current_mapped_block) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unmap previous block if any
|
||||
if (mapped_ptr) {
|
||||
ESP_LOGD(TAG, "- Unmapping block %u before mapping new block %u",
|
||||
current_mapped_block, block_index);
|
||||
unmap(); // Unmap previous block
|
||||
}
|
||||
|
||||
// Allocate map range
|
||||
ESP_LOGD(TAG, "- Allocating map range for block %u", block_index);
|
||||
esp_err_t err = esp_himem_alloc_map_range(ESP_HIMEM_BLKSZ, &range);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to allocate map range: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map the current block
|
||||
ESP_LOGD(TAG, "- Mapping block %u (offset %u)", block_index,
|
||||
block_index * ESP_HIMEM_BLKSZ);
|
||||
err = esp_himem_map(handle, range, block_index * ESP_HIMEM_BLKSZ, 0,
|
||||
ESP_HIMEM_BLKSZ, ESP_HIMEM_PROT_RW, &mapped_ptr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to map memory: %d", err);
|
||||
esp_himem_free_map_range(range);
|
||||
range = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
current_mapped_block = block_index;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esp32_psram
|
||||
251
lib/esp32-psram/src/esp32-psram/InMemoryFS.h_
Normal file
251
lib/esp32-psram/src/esp32-psram/InMemoryFS.h_
Normal file
@@ -0,0 +1,251 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "InMemoryFile.h"
|
||||
|
||||
// Define Arduino file mode constants if not already defined
|
||||
#ifndef FILE_READ
|
||||
#define FILE_READ 0
|
||||
#endif
|
||||
#ifndef FILE_WRITE
|
||||
#define FILE_WRITE 1
|
||||
#endif
|
||||
#ifndef FILE_APPEND
|
||||
#define FILE_APPEND 2
|
||||
#endif
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
/**
|
||||
* @class InMemoryFS
|
||||
* @brief Base class for memory-based file systems like PSRAM and HIMEM
|
||||
*
|
||||
* This class provides a common interface for in-memory file systems,
|
||||
* with methods for file management and traversal.
|
||||
*
|
||||
* @tparam VectorType The vector implementation to use (VectorPSRAM or
|
||||
* VectorHIMEM)
|
||||
* @tparam FileType The file implementation to return (FilePSRAM or FileHIMEM)
|
||||
*/
|
||||
template <typename VectorType, typename FileType>
|
||||
class InMemoryFS {
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize the file system
|
||||
* @return true if initialization was successful, false otherwise
|
||||
*/
|
||||
virtual bool begin() = 0;
|
||||
|
||||
/**
|
||||
* @brief Check if a file exists
|
||||
* @param filename Name of the file to check
|
||||
* @return true if the file exists, false otherwise
|
||||
*/
|
||||
bool exists(const char* filename) {
|
||||
if (!initialized) return false;
|
||||
return fileData.find(std::string(filename)) != fileData.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open a file
|
||||
* @param filename Name of the file to open
|
||||
* @param mode Mode to open the file in (FILE_READ, FILE_WRITE, etc.)
|
||||
* @return A file object for the opened file
|
||||
*/
|
||||
FileType open(const char* filename, uint8_t mode) {
|
||||
ESP_LOGD("InMemoryFS", "Opening file %s with mode %d", filename, mode);
|
||||
|
||||
if (!initialized) {
|
||||
ESP_LOGW("InMemoryFS", "Filesystem not initialized");
|
||||
FileType emptyFile;
|
||||
return emptyFile;
|
||||
}
|
||||
|
||||
// Convert Arduino file modes to our enum
|
||||
FileMode fileMode;
|
||||
if (mode == FILE_READ) {
|
||||
fileMode = FileMode::READ;
|
||||
} else if (mode == FILE_WRITE) {
|
||||
fileMode = FileMode::WRITE;
|
||||
} else if (mode == FILE_APPEND) {
|
||||
fileMode = FileMode::APPEND;
|
||||
} else {
|
||||
fileMode = FileMode::READ_WRITE;
|
||||
}
|
||||
|
||||
std::string filenameStr(filename);
|
||||
auto it = fileData.find(filenameStr);
|
||||
FileType file;
|
||||
|
||||
if (it != fileData.end()) {
|
||||
// File exists, create a new file pointing to existing data
|
||||
ESP_LOGD("InMemoryFS", "File exists, connecting to existing data");
|
||||
file.setVector(&(it->second));
|
||||
} else if (mode != FILE_READ) {
|
||||
// File doesn't exist, create it for writing or appending
|
||||
ESP_LOGD("InMemoryFS", "Creating new file for writing");
|
||||
fileData[filenameStr] = VectorType(); // Create empty vector in the map
|
||||
file.setVector(&(fileData[filenameStr]));
|
||||
} else {
|
||||
// File doesn't exist and mode is READ
|
||||
ESP_LOGW("InMemoryFS", "File doesn't exist and mode is READ");
|
||||
return file; // Return empty file
|
||||
}
|
||||
|
||||
// Configure the file
|
||||
file.setName(filename);
|
||||
file.open(fileMode);
|
||||
|
||||
// Set up the next file callback
|
||||
file.setNextFileCallback(
|
||||
[this](const char* currentFileName, FileMode currentMode) -> FileType {
|
||||
ESP_LOGD("InMemoryFS", "NextFile callback called for %s",
|
||||
currentFileName);
|
||||
|
||||
// Get the next filename
|
||||
String nextName = this->getNextFileName(currentFileName);
|
||||
if (nextName.isEmpty()) {
|
||||
ESP_LOGD("InMemoryFS", "No next file found");
|
||||
return FileType(); // Return empty file if no next file
|
||||
}
|
||||
|
||||
// Convert FileMode to Arduino mode
|
||||
uint8_t arduinoMode;
|
||||
if (currentMode == FileMode::READ)
|
||||
arduinoMode = FILE_READ;
|
||||
else if (currentMode == FileMode::WRITE)
|
||||
arduinoMode = FILE_WRITE;
|
||||
else if (currentMode == FileMode::APPEND)
|
||||
arduinoMode = FILE_APPEND;
|
||||
else
|
||||
arduinoMode = FILE_READ | FILE_WRITE;
|
||||
|
||||
// Open and return the next file
|
||||
ESP_LOGD("InMemoryFS", "Returning next file: %s", nextName.c_str());
|
||||
return this->open(nextName.c_str(), arduinoMode);
|
||||
});
|
||||
|
||||
ESP_LOGD("InMemoryFS", "File opened successfully");
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove a file
|
||||
* @param filename Name of the file to remove
|
||||
* @return true if the file was removed, false otherwise
|
||||
*/
|
||||
bool remove(const char* filename) {
|
||||
if (!initialized) return false;
|
||||
|
||||
std::string filenameStr(filename);
|
||||
auto it = fileData.find(filenameStr);
|
||||
|
||||
if (it != fileData.end()) {
|
||||
fileData.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a directory (no-op for compatibility)
|
||||
* @param dirname Name of the directory
|
||||
* @return Always returns true for compatibility
|
||||
*/
|
||||
bool mkdir(const char* dirname) {
|
||||
// No directories in this implementation, just for compatibility
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove a directory (no-op for compatibility)
|
||||
* @param dirname Name of the directory
|
||||
* @return Always returns true for compatibility
|
||||
*/
|
||||
bool rmdir(const char* dirname) {
|
||||
// No directories in this implementation, just for compatibility
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the next file after the specified file
|
||||
* @param currentFileName Name of the current file
|
||||
* @return Name of the next file, or empty string if there are no more files
|
||||
*/
|
||||
String getNextFileName(const char* currentFileName) {
|
||||
if (!initialized || fileData.empty()) {
|
||||
return String();
|
||||
}
|
||||
|
||||
if (currentFileName == nullptr || strlen(currentFileName) == 0 ||
|
||||
strcmp(currentFileName, "/") == 0) {
|
||||
// Return the first file if current is empty or is root directory
|
||||
return String(fileData.begin()->first.c_str());
|
||||
}
|
||||
|
||||
std::string current(currentFileName);
|
||||
auto it = fileData.find(current);
|
||||
|
||||
if (it == fileData.end()) {
|
||||
// Current file not found, find the next one alphabetically
|
||||
for (auto& entry : fileData) {
|
||||
if (entry.first > current) {
|
||||
return String(entry.first.c_str());
|
||||
}
|
||||
}
|
||||
// No file after the specified name
|
||||
return String();
|
||||
} else {
|
||||
// Found the current file, get the next one
|
||||
++it;
|
||||
if (it != fileData.end()) {
|
||||
return String(it->first.c_str());
|
||||
} else {
|
||||
// Was the last file
|
||||
return String();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the first file in the filesystem
|
||||
* @return Name of the first file, or empty string if there are no files
|
||||
*/
|
||||
String getFirstFileName() {
|
||||
if (!initialized || fileData.empty()) {
|
||||
return String();
|
||||
}
|
||||
return String(fileData.begin()->first.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the total number of files
|
||||
* @return Number of files in the filesystem
|
||||
*/
|
||||
size_t fileCount() const {
|
||||
if (!initialized) return 0;
|
||||
return fileData.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get total space
|
||||
* @return Total memory size in bytes
|
||||
*/
|
||||
virtual uint64_t totalBytes() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get free space
|
||||
* @return Free memory size in bytes
|
||||
*/
|
||||
virtual uint64_t freeBytes() = 0;
|
||||
|
||||
protected:
|
||||
bool initialized = false;
|
||||
std::map<std::string, VectorType> fileData;
|
||||
};
|
||||
|
||||
} // namespace esp32_psram
|
||||
421
lib/esp32-psram/src/esp32-psram/InMemoryFile.h_
Normal file
421
lib/esp32-psram/src/esp32-psram/InMemoryFile.h_
Normal file
@@ -0,0 +1,421 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "VectorHIMEM.h"
|
||||
#include "VectorPSRAM.h"
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
// Define file modes
|
||||
enum class FileMode { READ, WRITE, APPEND, READ_WRITE };
|
||||
|
||||
/**
|
||||
* @class InMemoryFile
|
||||
* @brief A file-like interface for vector-backed storage in memory
|
||||
*
|
||||
* This class provides a file-like interface (compatible with Arduino's Stream)
|
||||
* for reading and writing data to vector-backed storage. It can use either
|
||||
* standard PSRAM or ESP32's HIMEM (high memory beyond the 4MB boundary)
|
||||
* as its underlying storage mechanism through the VectorPSRAM and VectorHIMEM
|
||||
* implementations.
|
||||
*
|
||||
* InMemoryFile offers familiar file operations like open, close, read, write,
|
||||
* seek, and truncate while managing the data in memory rather than on disk.
|
||||
* This makes it useful for temporary storage, data processing, or situations
|
||||
* where file operations are needed but filesystem access is not available
|
||||
* or desirable.
|
||||
*
|
||||
* The class can either manage its own internal vector or reference an external
|
||||
* vector supplied by another component (like PSRAMClass or HIMEMClass).
|
||||
*
|
||||
* @tparam VectorType The vector implementation to use for storage (typically
|
||||
* VectorPSRAM<uint8_t> or VectorHIMEM<uint8_t>)
|
||||
*
|
||||
* @note The performance characteristics will depend on the underlying vector
|
||||
* implementation. HIMEM operations are generally slower than regular
|
||||
* PSRAM operations but allow for larger storage capacity.
|
||||
*/
|
||||
template <typename VectorType>
|
||||
class InMemoryFile : public Stream {
|
||||
public:
|
||||
// Define a single callback type that returns the next file directly
|
||||
using NextFileCallback =
|
||||
std::function<InMemoryFile<VectorType>(const char*, FileMode)>;
|
||||
/**
|
||||
* @brief Default constructor - initializes with internal vector
|
||||
*/
|
||||
InMemoryFile() : data_ptr(&internal_data) {
|
||||
ESP_LOGD(TAG, "InMemoryFile constructor called");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor with filename and mode
|
||||
* @param filename Name of the file
|
||||
* @param mode Mode to open the file in
|
||||
*/
|
||||
InMemoryFile(const char* filename, FileMode mode)
|
||||
: data_ptr(&internal_data), name_(filename) {
|
||||
ESP_LOGD(TAG, "InMemoryFile constructor with name and mode called");
|
||||
open(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor - ensures proper cleanup
|
||||
*/
|
||||
virtual ~InMemoryFile() {
|
||||
ESP_LOGD(TAG, "InMemoryFile destructor called");
|
||||
close();
|
||||
if (!using_external_vector) {
|
||||
data_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the vector to use for this file
|
||||
* @param vec Pointer to the vector to use
|
||||
*/
|
||||
void setVector(VectorType* vec) {
|
||||
ESP_LOGD(TAG, "Setting external vector pointer: %p", vec);
|
||||
if (vec) {
|
||||
data_ptr = vec;
|
||||
using_external_vector = true;
|
||||
} else {
|
||||
data_ptr = &internal_data;
|
||||
using_external_vector = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the name of this file
|
||||
* @param name The name to set
|
||||
*/
|
||||
void setName(const char* name) {
|
||||
ESP_LOGD(TAG, "Setting file name: %s", name);
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open the file with the specified mode
|
||||
* @param mode Mode to open the file in
|
||||
* @return true if opened successfully, false otherwise
|
||||
*/
|
||||
bool open(FileMode mode) {
|
||||
ESP_LOGD(TAG, "Opening file '%s' with mode %d", name_.c_str(),
|
||||
static_cast<int>(mode));
|
||||
this->mode = mode;
|
||||
|
||||
if (mode == FileMode::WRITE) {
|
||||
// Clear the vector for writing
|
||||
data_ptr->clear();
|
||||
position_ = 0;
|
||||
} else if (mode == FileMode::APPEND) {
|
||||
// Position at end for appending
|
||||
position_ = data_ptr->size();
|
||||
} else {
|
||||
// READ or READ_WRITE - position at beginning
|
||||
position_ = 0;
|
||||
}
|
||||
|
||||
open_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the file
|
||||
*/
|
||||
void close() {
|
||||
ESP_LOGD(TAG, "Closing file '%s'", name_.c_str());
|
||||
open_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if file is open
|
||||
* @return true if open, false otherwise
|
||||
*/
|
||||
bool isOpen() const { return open_; }
|
||||
|
||||
/**
|
||||
* @brief Get the name of the file
|
||||
* @return File name
|
||||
*/
|
||||
String name() const { return name_; }
|
||||
|
||||
/**
|
||||
* @brief Get the size of the file
|
||||
* @return File size in bytes
|
||||
*/
|
||||
size_t size() const { return data_ptr->size(); }
|
||||
|
||||
// Stream interface implementation
|
||||
|
||||
/**
|
||||
* @brief Read a single byte from the file
|
||||
* @return The next byte, or -1 if no data is available
|
||||
*/
|
||||
int read() override {
|
||||
ESP_LOGD(TAG, "InMemoryFile::read() - position %u", (unsigned)position_);
|
||||
if (!open_ || (mode != FileMode::READ && mode != FileMode::READ_WRITE)) {
|
||||
ESP_LOGE(TAG, "read failed: file not open for reading");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (position_ >= data_ptr->size()) {
|
||||
ESP_LOGD(TAG, "read at EOF: pos=%u, size=%u", (unsigned)position_,
|
||||
(unsigned)data_ptr->size());
|
||||
return -1; // EOF
|
||||
}
|
||||
|
||||
uint8_t byte = (*data_ptr)[position_++];
|
||||
return byte;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read multiple bytes from the file
|
||||
* @param buffer Buffer to store the read data
|
||||
* @param size Maximum number of bytes to read
|
||||
* @return Number of bytes actually read
|
||||
*/
|
||||
size_t readBytes(char* buffer, size_t size) override {
|
||||
ESP_LOGD(TAG, "InMemoryFile::readBytes: %u", (unsigned)size);
|
||||
if (!open_ || (mode != FileMode::READ && mode != FileMode::READ_WRITE)) {
|
||||
ESP_LOGE(TAG, "read failed: file not open for reading");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate how many bytes we can actually read
|
||||
size_t available_bytes = data_ptr->size() - position_;
|
||||
size_t bytes_to_read = min(size, available_bytes);
|
||||
|
||||
// Read the bytes
|
||||
for (size_t i = 0; i < bytes_to_read; i++) {
|
||||
buffer[i] = (*data_ptr)[position_ + i];
|
||||
}
|
||||
|
||||
position_ += bytes_to_read;
|
||||
return bytes_to_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of bytes available to read
|
||||
* @return Number of bytes available
|
||||
*/
|
||||
int available() override {
|
||||
if (!open_) return 0;
|
||||
return data_ptr->size() - position_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Peek at the next byte without advancing the position
|
||||
* @return The next byte, or -1 if no data is available
|
||||
*/
|
||||
int peek() override {
|
||||
if (!open_ || (mode != FileMode::READ && mode != FileMode::READ_WRITE)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (position_ >= data_ptr->size()) {
|
||||
return -1; // EOF
|
||||
}
|
||||
|
||||
return (*data_ptr)[position_];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ensure all data is committed to the storage
|
||||
* No-op for this implementation since data is in memory
|
||||
*/
|
||||
void flush() override {
|
||||
// No-op for in-memory implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a single byte to the file
|
||||
* @param b The byte to write
|
||||
* @return 1 if the byte was written, 0 otherwise
|
||||
*/
|
||||
size_t write(uint8_t b) override {
|
||||
ESP_LOGD(TAG, "InMemoryFile::write: 1 byte");
|
||||
if (!open_ || (mode != FileMode::WRITE && mode != FileMode::APPEND &&
|
||||
mode != FileMode::READ_WRITE)) {
|
||||
ESP_LOGE(TAG, "write failed: file not open for writing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (position_ >= data_ptr->size()) {
|
||||
// Append to the end
|
||||
data_ptr->push_back(b);
|
||||
} else {
|
||||
// Replace existing byte
|
||||
(*data_ptr)[position_] = b;
|
||||
}
|
||||
|
||||
position_++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write multiple bytes to the file
|
||||
* @param buffer Buffer containing the data to write
|
||||
* @param size Number of bytes to write
|
||||
* @return Number of bytes actually written
|
||||
*/
|
||||
size_t write(const uint8_t* buffer, size_t size) override {
|
||||
ESP_LOGD(TAG, "InMemoryFile::write: %u", (unsigned)size);
|
||||
if (!open_ || (mode != FileMode::WRITE && mode != FileMode::APPEND &&
|
||||
mode != FileMode::READ_WRITE)) {
|
||||
ESP_LOGE(TAG, "write failed: file not open for writing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (position_ >= data_ptr->size()) {
|
||||
// Append to the end
|
||||
data_ptr->reserve(data_ptr->size() + size); // Optimize capacity
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
data_ptr->push_back(buffer[i]);
|
||||
}
|
||||
} else {
|
||||
// Replace/insert bytes
|
||||
size_t space_available = data_ptr->size() - position_;
|
||||
size_t bytes_to_replace = min(size, space_available);
|
||||
size_t bytes_to_append = size - bytes_to_replace;
|
||||
|
||||
// Replace existing bytes
|
||||
for (size_t i = 0; i < bytes_to_replace; i++) {
|
||||
(*data_ptr)[position_ + i] = buffer[i];
|
||||
}
|
||||
|
||||
// Append remaining bytes
|
||||
for (size_t i = 0; i < bytes_to_append; i++) {
|
||||
data_ptr->push_back(buffer[bytes_to_replace + i]);
|
||||
}
|
||||
}
|
||||
|
||||
position_ += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
// File-specific methods
|
||||
|
||||
/**
|
||||
* @brief Set the current position in the file
|
||||
* @param pos The position to seek to
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool seek(size_t pos) {
|
||||
ESP_LOGD(TAG, "InMemoryFile::seek: %u", (unsigned)pos);
|
||||
if (!open_ || pos > data_ptr->size()) {
|
||||
ESP_LOGE(TAG, "seek failed: file not open or position beyond size");
|
||||
return false;
|
||||
}
|
||||
|
||||
position_ = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current position in the file
|
||||
* @return Current position
|
||||
*/
|
||||
size_t position() const { return position_; }
|
||||
|
||||
/**
|
||||
* @brief Truncate the file to the current position
|
||||
*/
|
||||
void truncate() {
|
||||
ESP_LOGD(TAG, "InMemoryFile::truncate at position %u", (unsigned)position_);
|
||||
if (!open_ || (mode != FileMode::WRITE && mode != FileMode::READ_WRITE)) {
|
||||
ESP_LOGE(TAG, "truncate failed: file not open for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
if (position_ < data_ptr->size()) {
|
||||
// Create a new vector with just the data up to position_
|
||||
VectorType new_data;
|
||||
new_data.reserve(position_);
|
||||
for (size_t i = 0; i < position_; i++) {
|
||||
new_data.push_back((*data_ptr)[i]);
|
||||
}
|
||||
|
||||
// Replace the old data
|
||||
*data_ptr = std::move(new_data);
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const { return open_; }
|
||||
/**
|
||||
* @brief Set callback for navigating to the next file
|
||||
*
|
||||
* This function sets a callback that will be used to retrieve the next
|
||||
* file when getNextFile() is called. The callback takes the current file name
|
||||
* and mode, and returns the next file object directly.
|
||||
*
|
||||
* @param callback Function that returns the next file
|
||||
*/
|
||||
void setNextFileCallback(NextFileCallback callback) {
|
||||
ESP_LOGD(TAG, "Setting next file callback");
|
||||
nextFileCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next file in the filesystem
|
||||
*
|
||||
* This method uses the registered callback to get the next file
|
||||
* after this one in the filesystem.
|
||||
*
|
||||
* @return The next file, or an empty file if there is no next file or
|
||||
* callback isn't set
|
||||
*/
|
||||
InMemoryFile<VectorType> getNextFile() {
|
||||
if (!nextFileCallback || name_.isEmpty()) {
|
||||
// Return empty file if callback isn't set or no name
|
||||
InMemoryFile<VectorType> emptyFile;
|
||||
return emptyFile;
|
||||
}
|
||||
|
||||
// Get the next file directly using the callback
|
||||
return nextFileCallback(name_.c_str(), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserve storage
|
||||
* @param new_cap The new capacity of the file
|
||||
*/
|
||||
bool reserve(size_t new_cap) {
|
||||
if (data_ptr == nullptr) return false;
|
||||
data_ptr->reserve(new_cap);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of bytes that can be held in current storage.
|
||||
* Please note that this might dynamically grow!
|
||||
* @return The capacity of the file
|
||||
*/
|
||||
size_t capacity() const {
|
||||
if (data_ptr == nullptr) return 0;
|
||||
return data_ptr->capacity();
|
||||
}
|
||||
|
||||
private:
|
||||
VectorType* data_ptr =
|
||||
nullptr; // Pointer to vector data (internal or external)
|
||||
VectorType internal_data; // Internal vector for standalone use
|
||||
bool using_external_vector = false;
|
||||
size_t position_ = 0;
|
||||
bool open_ = false;
|
||||
FileMode mode = FileMode::READ;
|
||||
String name_;
|
||||
|
||||
// Single callback for getting the next file
|
||||
NextFileCallback nextFileCallback = nullptr;
|
||||
|
||||
// Tags for debug logging
|
||||
static constexpr const char* TAG = "InMemoryFile";
|
||||
};
|
||||
|
||||
// Type aliases for convenience
|
||||
using FilePSRAM = InMemoryFile<VectorPSRAM<uint8_t>>;
|
||||
using FileHIMEM = InMemoryFile<VectorHIMEM<uint8_t>>;
|
||||
|
||||
} // namespace esp32_psram
|
||||
54
lib/esp32-psram/src/esp32-psram/PSRAM.h_
Normal file
54
lib/esp32-psram/src/esp32-psram/PSRAM.h_
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "InMemoryFS.h"
|
||||
#include "VectorPSRAM.h"
|
||||
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
/**
|
||||
* @class PSRAMClass
|
||||
* @brief Class for managing files stored in ESP32's PSRAM
|
||||
*
|
||||
* This class provides an interface similar to SD.h for managing files
|
||||
* that are stored in PSRAM memory rather than on an SD card.
|
||||
*/
|
||||
class PSRAMClass : public InMemoryFS<VectorPSRAM<uint8_t>, FilePSRAM> {
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize the PSRAM filesystem
|
||||
* @return true if initialization was successful, false otherwise
|
||||
*/
|
||||
bool begin() override {
|
||||
if (ESP.getFreePsram() > 0) {
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
initialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get total space (returns available PSRAM)
|
||||
* @return Total PSRAM size in bytes
|
||||
*/
|
||||
uint64_t totalBytes() override {
|
||||
return ESP.getPsramSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get free space (returns free PSRAM)
|
||||
* @return Free PSRAM size in bytes
|
||||
*/
|
||||
uint64_t freeBytes() override {
|
||||
return ESP.getFreePsram();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Global instance of PSRAMClass for easy access
|
||||
*/
|
||||
static PSRAMClass PSRAM;
|
||||
|
||||
} // namespace esp32_psram
|
||||
250
lib/esp32-psram/src/esp32-psram/RingBufferStream.h
Normal file
250
lib/esp32-psram/src/esp32-psram/RingBufferStream.h
Normal file
@@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Stream.h>
|
||||
#include "VectorPSRAM.h"
|
||||
#include "VectorHIMEM.h"
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
/**
|
||||
* @class RingBufferStream
|
||||
* @brief A circular buffer implementation that extends Arduino's Stream
|
||||
* @tparam VectorType The vector type to use as underlying storage
|
||||
*
|
||||
* This class implements a ring buffer (circular buffer) that uses a vector container
|
||||
* for storage. It extends Arduino's Stream class to provide standard stream functionality.
|
||||
* The buffer can be used with any vector type, including VectorPSRAM and VectorHIMEM.
|
||||
*/
|
||||
template <typename VectorType>
|
||||
class RingBufferStream : public Stream {
|
||||
private:
|
||||
VectorType buffer;
|
||||
size_t readIndex = 0;
|
||||
size_t writeIndex = 0;
|
||||
bool full = false;
|
||||
size_t maxSize;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor with specified buffer size
|
||||
* @param size The size of the buffer in bytes
|
||||
*/
|
||||
RingBufferStream(size_t size) : maxSize(size) {
|
||||
buffer.resize(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of bytes available for reading
|
||||
* @return Number of bytes available
|
||||
*/
|
||||
int available() override {
|
||||
if (full) {
|
||||
return maxSize;
|
||||
}
|
||||
if (writeIndex >= readIndex) {
|
||||
return writeIndex - readIndex;
|
||||
} else {
|
||||
return maxSize - (readIndex - writeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a byte from the buffer
|
||||
* @return The byte read, or -1 if the buffer is empty
|
||||
*/
|
||||
int read() override {
|
||||
if (isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t value = buffer[readIndex];
|
||||
readIndex = (readIndex + 1) % maxSize;
|
||||
full = false;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Look at the next byte in the buffer without removing it
|
||||
* @return The next byte to be read, or -1 if the buffer is empty
|
||||
*/
|
||||
int peek() override {
|
||||
if (isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return buffer[readIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a byte to the buffer
|
||||
* @param value The byte to write
|
||||
* @return 1 if the byte was written, 0 if the buffer is full
|
||||
*/
|
||||
size_t write(uint8_t value) override {
|
||||
if (full) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer[writeIndex] = value;
|
||||
writeIndex = (writeIndex + 1) % maxSize;
|
||||
|
||||
// Check if buffer is now full
|
||||
if (writeIndex == readIndex) {
|
||||
full = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write multiple bytes to the buffer
|
||||
* @param data Pointer to the data to write
|
||||
* @param size Number of bytes to write
|
||||
* @return Number of bytes written
|
||||
*/
|
||||
size_t write(const uint8_t *data, size_t size) {
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (write(data[i])) {
|
||||
bytesWritten++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the buffer, removing all content
|
||||
*/
|
||||
void flush() override {
|
||||
readIndex = 0;
|
||||
writeIndex = 0;
|
||||
full = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the buffer is empty
|
||||
* @return true if the buffer is empty, false otherwise
|
||||
*/
|
||||
bool isEmpty() const {
|
||||
return !full && (readIndex == writeIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the buffer is full
|
||||
* @return true if the buffer is full, false otherwise
|
||||
*/
|
||||
bool isFull() const {
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of bytes that can be written without blocking
|
||||
* @return Number of bytes available for writing
|
||||
*/
|
||||
int availableForWrite() override {
|
||||
if (full) {
|
||||
return 0;
|
||||
}
|
||||
if (readIndex > writeIndex) {
|
||||
return readIndex - writeIndex;
|
||||
} else {
|
||||
return maxSize - (writeIndex - readIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the total capacity of the buffer
|
||||
* @return Total buffer capacity in bytes
|
||||
*/
|
||||
size_t size() const {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of bytes currently in the buffer
|
||||
* @return Number of bytes in the buffer
|
||||
*/
|
||||
size_t used() const {
|
||||
return available();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of bytes still available in the buffer
|
||||
* @return Number of free bytes in the buffer
|
||||
*/
|
||||
size_t free() const {
|
||||
return maxSize - available();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read multiple bytes from the buffer
|
||||
* @param buffer Buffer to store the read data
|
||||
* @param size Maximum number of bytes to read
|
||||
* @return Number of bytes actually read
|
||||
*/
|
||||
size_t readBytes(char* buffer, size_t size) override {
|
||||
size_t bytesRead = 0;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
int value = read();
|
||||
if (value >= 0) {
|
||||
buffer[i] = (char)value;
|
||||
bytesRead++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read multiple bytes from the buffer
|
||||
* @param buffer Buffer to store the read data
|
||||
* @param size Maximum number of bytes to read
|
||||
* @return Number of bytes actually read
|
||||
*/
|
||||
size_t readBytes(uint8_t* buffer, size_t size) {
|
||||
return readBytes(reinterpret_cast<char*>(buffer), size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get direct access to the underlying vector
|
||||
* @return Reference to the underlying vector
|
||||
*/
|
||||
VectorType& getVector() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get const access to the underlying vector
|
||||
* @return Const reference to the underlying vector
|
||||
*/
|
||||
const VectorType& getVector() const {
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type alias for a RingBufferStream that uses PSRAM-backed vector storage
|
||||
*/
|
||||
using RingBufferStreamPSRAM = RingBufferStream<VectorPSRAM<uint8_t>>;
|
||||
|
||||
/**
|
||||
* @brief Type alias for a RingBufferStream that uses HIMEM-backed vector storage
|
||||
*/
|
||||
using RingBufferStreamHIMEM = RingBufferStream<VectorHIMEM<uint8_t>>;
|
||||
|
||||
/**
|
||||
* @brief Type alias for a RingBufferStream that uses std::vector storage
|
||||
*/
|
||||
|
||||
using RingBufferStreamRAM = RingBufferStream<std::vector<uint8_t>>;
|
||||
|
||||
|
||||
} // namespace esp32_psram
|
||||
227
lib/esp32-psram/src/esp32-psram/TypedRingBuffer.h
Normal file
227
lib/esp32-psram/src/esp32-psram/TypedRingBuffer.h
Normal file
@@ -0,0 +1,227 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
#include "VectorPSRAM.h"
|
||||
#include "VectorHIMEM.h"
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
/**
|
||||
* @class TypedRingBuffer
|
||||
* @brief A generic ring buffer implementation for any data type
|
||||
* @tparam T The data type to store in the buffer
|
||||
* @tparam VectorType The vector type to use as underlying storage
|
||||
*
|
||||
* This class implements a ring buffer (circular buffer) that uses a vector container
|
||||
* for storage. Unlike the Stream-based RingBuffer, this can store any data type.
|
||||
*/
|
||||
template <typename T, typename VectorType>
|
||||
class TypedRingBuffer {
|
||||
private:
|
||||
VectorType buffer;
|
||||
size_t readIndex = 0;
|
||||
size_t writeIndex = 0;
|
||||
bool full = false;
|
||||
size_t maxSize;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor with specified buffer capacity
|
||||
* @param capacity The maximum number of elements the buffer can hold
|
||||
*/
|
||||
TypedRingBuffer(size_t capacity) : maxSize(capacity) {
|
||||
buffer.resize(capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push an element to the buffer
|
||||
* @param value The value to add
|
||||
* @return true if the element was added, false if the buffer is full
|
||||
*/
|
||||
bool push(const T& value) {
|
||||
if (full) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer[writeIndex] = value;
|
||||
advanceWriteIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push an element to the buffer, overwriting oldest data if full
|
||||
* @param value The value to add
|
||||
* @return true if an old element was overwritten, false otherwise
|
||||
*/
|
||||
bool pushOverwrite(const T& value) {
|
||||
bool overwritten = full;
|
||||
|
||||
buffer[writeIndex] = value;
|
||||
advanceWriteIndex();
|
||||
|
||||
return overwritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pop an element from the buffer
|
||||
* @param value Reference to store the popped value
|
||||
* @return true if an element was popped, false if the buffer is empty
|
||||
*/
|
||||
bool pop(T& value) {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = buffer[readIndex];
|
||||
advanceReadIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Peek at the next element without removing it
|
||||
* @param value Reference to store the peeked value
|
||||
* @return true if an element was peeked, false if the buffer is empty
|
||||
*/
|
||||
bool peek(T& value) const {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = buffer[readIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the element at a specific index relative to the read position
|
||||
* @param index The relative index from current read position
|
||||
* @param value Reference to store the value
|
||||
* @return true if the element exists, false otherwise
|
||||
*/
|
||||
bool peekAt(size_t index, T& value) const {
|
||||
if (isEmpty() || index >= available()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t actualIndex = (readIndex + index) % maxSize;
|
||||
value = buffer[actualIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the buffer, removing all content
|
||||
*/
|
||||
void clear() {
|
||||
readIndex = 0;
|
||||
writeIndex = 0;
|
||||
full = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the buffer is empty
|
||||
* @return true if the buffer is empty, false otherwise
|
||||
*/
|
||||
bool isEmpty() const {
|
||||
return !full && (readIndex == writeIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the buffer is full
|
||||
* @return true if the buffer is full, false otherwise
|
||||
*/
|
||||
bool isFull() const {
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of elements in the buffer
|
||||
* @return Number of elements in the buffer
|
||||
*/
|
||||
size_t available() const {
|
||||
if (full) {
|
||||
return maxSize;
|
||||
}
|
||||
if (writeIndex >= readIndex) {
|
||||
return writeIndex - readIndex;
|
||||
} else {
|
||||
return maxSize - (readIndex - writeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of empty slots in the buffer
|
||||
* @return Number of empty slots
|
||||
*/
|
||||
size_t availableForWrite() const {
|
||||
return maxSize - available();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the total capacity of the buffer
|
||||
* @return Total buffer capacity
|
||||
*/
|
||||
size_t capacity() const {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get direct access to the underlying vector
|
||||
* @return Reference to the underlying vector
|
||||
*/
|
||||
VectorType& getVector() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get const access to the underlying vector
|
||||
* @return Const reference to the underlying vector
|
||||
*/
|
||||
const VectorType& getVector() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Advance the write index
|
||||
*/
|
||||
void advanceWriteIndex() {
|
||||
writeIndex = (writeIndex + 1) % maxSize;
|
||||
if (writeIndex == readIndex) {
|
||||
full = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Advance the read index
|
||||
*/
|
||||
void advanceReadIndex() {
|
||||
readIndex = (readIndex + 1) % maxSize;
|
||||
full = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type alias for a typed ring buffer that uses std::vector (in RAM)
|
||||
*/
|
||||
template<typename T>
|
||||
using TypedRingBufferRAM = TypedRingBuffer<T, std::vector<T>>;
|
||||
/**
|
||||
* @brief Type alias for a typed ring buffer that uses HIMEM-backed vector storage
|
||||
*/
|
||||
template<typename T>
|
||||
using TypedRingBufferHIMEM = TypedRingBuffer<T, VectorHIMEM<T>>;
|
||||
|
||||
/**
|
||||
* @brief Type alias for a typed ring buffer that uses PSRAM-backed vector storage
|
||||
*/
|
||||
template<typename T>
|
||||
using TypedRingBufferPSRAM = TypedRingBuffer<T, VectorPSRAM<T>>;
|
||||
|
||||
/**
|
||||
* @brief Type alias for a typed ring buffer that uses HIMEM-backed vector storage
|
||||
*/
|
||||
// template<typename T>
|
||||
// using TypedRingBufferHIMEM = TypedRingBuffer<T, VectorHIMEM<T>>;
|
||||
|
||||
} // namespace esp32_psram
|
||||
529
lib/esp32-psram/src/esp32-psram/VectorHIMEM.h
Normal file
529
lib/esp32-psram/src/esp32-psram/VectorHIMEM.h
Normal file
@@ -0,0 +1,529 @@
|
||||
#pragma once
|
||||
|
||||
#include "HimemBlock.h"
|
||||
|
||||
namespace esp32_psram {
|
||||
|
||||
|
||||
/**
|
||||
* @class VectorHIMEM
|
||||
* @brief Vector implementation that uses ESP32's high memory (himem) for
|
||||
* storage
|
||||
* @tparam T Type of elements stored in the vector
|
||||
*/
|
||||
template <typename T>
|
||||
class VectorHIMEM {
|
||||
public:
|
||||
// Type definitions
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
/**
|
||||
* @brief Default constructor - creates an empty vector
|
||||
*/
|
||||
VectorHIMEM() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a vector with the given number of default-initialized
|
||||
* elements
|
||||
* @param count The size of the vector
|
||||
*/
|
||||
explicit VectorHIMEM(size_type count) { resize(count); }
|
||||
|
||||
/**
|
||||
* @brief Constructs a vector with the given number of copies of a value
|
||||
* @param count The size of the vector
|
||||
* @param value The value to initialize elements with
|
||||
*/
|
||||
VectorHIMEM(size_type count, const T& value) { resize(count, value); }
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* @param other The vector to copy from
|
||||
*/
|
||||
VectorHIMEM(const VectorHIMEM& other) {
|
||||
if (other.element_count > 0) {
|
||||
if (reallocate(other.element_count)) {
|
||||
T temp;
|
||||
VectorHIMEM& other_ = const_cast<VectorHIMEM&>(other);
|
||||
for (size_t i = 0; i < other.element_count; ++i) {
|
||||
other_.memory.read((void*)&temp, i * sizeof(T), sizeof(T));
|
||||
memory.write((void*)&temp, i * sizeof(T), sizeof(T));
|
||||
}
|
||||
element_count = other.element_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
* @param other The vector to move from
|
||||
*/
|
||||
VectorHIMEM(VectorHIMEM&& other) noexcept
|
||||
: memory(std::move(other.memory)),
|
||||
element_count(other.element_count),
|
||||
element_capacity(other.element_capacity) {
|
||||
other.element_count = 0;
|
||||
other.element_capacity = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializer list constructor
|
||||
* @param init The initializer list to copy from
|
||||
*/
|
||||
VectorHIMEM(std::initializer_list<T> init) {
|
||||
if (init.size() > 0) {
|
||||
if (reallocate(init.size())) {
|
||||
size_t i = 0;
|
||||
for (const auto& item : init) {
|
||||
memory.write(&item, i * sizeof(T), sizeof(T));
|
||||
++i;
|
||||
}
|
||||
element_count = init.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor - ensures all elements are properly destroyed
|
||||
*/
|
||||
~VectorHIMEM() { clear(); }
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator
|
||||
* @param other The vector to copy from
|
||||
* @return Reference to this vector
|
||||
*/
|
||||
VectorHIMEM& operator=(const VectorHIMEM& other) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
if (other.element_count > 0) {
|
||||
if (reallocate(other.element_count)) {
|
||||
T temp;
|
||||
for (size_t i = 0; i < other.element_count; ++i) {
|
||||
const_cast<esp32_psram::HimemBlock*>(&other.memory)->read(&temp, i * sizeof(T), sizeof(T));
|
||||
memory.write(&temp, i * sizeof(T), sizeof(T));
|
||||
}
|
||||
element_count = other.element_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator
|
||||
* @param other The vector to move from
|
||||
* @return Reference to this vector
|
||||
*/
|
||||
VectorHIMEM& operator=(VectorHIMEM&& other) noexcept {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
memory = std::move(other.memory);
|
||||
element_count = other.element_count;
|
||||
element_capacity = other.element_capacity;
|
||||
other.element_count = 0;
|
||||
other.element_capacity = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializer list assignment operator
|
||||
* @param ilist The initializer list to copy from
|
||||
* @return Reference to this vector
|
||||
*/
|
||||
VectorHIMEM& operator=(std::initializer_list<T> ilist) {
|
||||
clear();
|
||||
if (ilist.size() > 0) {
|
||||
if (reallocate(ilist.size())) {
|
||||
size_t i = 0;
|
||||
for (const auto& item : ilist) {
|
||||
memory.write(&item, i * sizeof(T), sizeof(T));
|
||||
++i;
|
||||
}
|
||||
element_count = ilist.size();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element with bounds checking
|
||||
* @param pos The position of the element
|
||||
* @return Reference to the element at position pos
|
||||
* @throws std::out_of_range if pos is not within the range of the vector
|
||||
*/
|
||||
reference at(size_type pos) {
|
||||
if (pos >= element_count) {
|
||||
throw std::out_of_range("VectorHIMEM: index out of range");
|
||||
}
|
||||
static T temp;
|
||||
memory.read(&temp, pos * sizeof(T), sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element with bounds checking (const version)
|
||||
* @param pos The position of the element
|
||||
* @return Const reference to the element at position pos
|
||||
* @throws std::out_of_range if pos is not within the range of the vector
|
||||
*/
|
||||
const_reference at(size_type pos) const {
|
||||
if (pos >= element_count) {
|
||||
throw std::out_of_range("VectorHIMEM: index out of range");
|
||||
}
|
||||
static T temp;
|
||||
memory.read(&temp, pos * sizeof(T), sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element without bounds checking
|
||||
* @param pos The position of the element
|
||||
* @return Reference to the element at position pos
|
||||
*/
|
||||
reference operator[](size_type pos) {
|
||||
static T temp;
|
||||
memory.read(&temp, pos * sizeof(T), sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
// Add const version of operator[]
|
||||
const_reference operator[](size_type pos) const {
|
||||
static T result;
|
||||
// Need to cast away const for the read operation since it doesn't modify content
|
||||
HimemBlock& non_const_memory = const_cast<HimemBlock&>(memory);
|
||||
non_const_memory.read(&result, pos * sizeof(T), sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Access the first element
|
||||
* @return Reference to the first element
|
||||
*/
|
||||
reference front() {
|
||||
static T temp;
|
||||
memory.read(&temp, 0, sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access the first element (const version)
|
||||
* @return Const reference to the first element
|
||||
*/
|
||||
const_reference front() const {
|
||||
static T temp;
|
||||
memory.read(&temp, 0, sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access the last element
|
||||
* @return Reference to the last element
|
||||
*/
|
||||
reference back() {
|
||||
static T temp;
|
||||
memory.read(&temp, (element_count - 1) * sizeof(T), sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access the last element (const version)
|
||||
* @return Const reference to the last element
|
||||
*/
|
||||
const_reference back() const {
|
||||
static T temp;
|
||||
memory.read(&temp, (element_count - 1) * sizeof(T), sizeof(T));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the vector is empty
|
||||
* @return true if the vector is empty, false otherwise
|
||||
*/
|
||||
bool empty() const { return element_count == 0; }
|
||||
|
||||
/**
|
||||
* @brief Get the number of elements
|
||||
* @return The number of elements in the vector
|
||||
*/
|
||||
size_type size() const { return element_count; }
|
||||
|
||||
/**
|
||||
* @brief Get the maximum possible number of elements
|
||||
* @return The maximum possible number of elements the vector can hold
|
||||
*/
|
||||
size_type max_size() const {
|
||||
return std::numeric_limits<size_type>::max() / sizeof(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserve storage
|
||||
* @param new_cap The new capacity of the vector
|
||||
*/
|
||||
void reserve(size_type new_cap) {
|
||||
if (new_cap > element_capacity) {
|
||||
reallocate(new_cap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of elements that can be held in current storage
|
||||
* @return The capacity of the vector
|
||||
*/
|
||||
size_type capacity() const { return element_capacity; }
|
||||
|
||||
/**
|
||||
* @brief Clear the contents
|
||||
*/
|
||||
void clear() {
|
||||
// We're using plain memory so no destructors to call
|
||||
element_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an element to the end
|
||||
* @param value The value to append
|
||||
*/
|
||||
void push_back(const T& value) {
|
||||
if (element_count >= element_capacity) {
|
||||
size_t new_capacity =
|
||||
element_capacity == 0 ? min_elements : element_capacity * 2;
|
||||
if (!reallocate(new_capacity)) {
|
||||
ESP_LOGE(TAG, "Failed to reallocate for push_back");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the new element at the end
|
||||
memory.write(&value, element_count * sizeof(T), sizeof(T));
|
||||
++element_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an element to the end by moving it
|
||||
* @param value The value to append
|
||||
*/
|
||||
void push_back(T&& value) {
|
||||
// For POD types, this is the same as the const& version
|
||||
push_back(static_cast<const T&>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove the last element
|
||||
*/
|
||||
void pop_back() {
|
||||
if (element_count > 0) {
|
||||
--element_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Change the number of elements stored
|
||||
* @param count The new size of the vector
|
||||
*/
|
||||
void resize(size_type count) {
|
||||
if (count > element_capacity) {
|
||||
if (!reallocate(count)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
element_count = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Change the number of elements stored
|
||||
* @param count The new size of the vector
|
||||
* @param value The value to initialize new elements with
|
||||
*/
|
||||
void resize(size_type count, const value_type& value) {
|
||||
size_t old_size = element_count;
|
||||
|
||||
if (count > element_count) {
|
||||
// Need to add elements
|
||||
if (count > element_capacity) {
|
||||
if (!reallocate(count)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize new elements with value
|
||||
for (size_t i = old_size; i < count; ++i) {
|
||||
memory.write(&value, i * sizeof(T), sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
element_count = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase an element
|
||||
* @param pos Index of the element to erase
|
||||
*/
|
||||
void erase(size_type pos) {
|
||||
if (pos >= element_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move each element down by one, overwriting the erased element
|
||||
T temp;
|
||||
for (size_t i = pos + 1; i < element_count; ++i) {
|
||||
memory.read(&temp, i * sizeof(T), sizeof(T));
|
||||
memory.write(&temp, (i - 1) * sizeof(T), sizeof(T));
|
||||
}
|
||||
|
||||
--element_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert an element at a specific position
|
||||
* @param pos Position where the element should be inserted
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void insert(size_type pos, const T& value) {
|
||||
if (pos > element_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (element_count >= element_capacity) {
|
||||
size_t new_capacity =
|
||||
element_capacity == 0 ? min_elements : element_capacity * 2;
|
||||
if (!reallocate(new_capacity)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Move elements up to make space
|
||||
T temp;
|
||||
for (size_t i = element_count; i > pos; --i) {
|
||||
memory.read(&temp, (i - 1) * sizeof(T), sizeof(T));
|
||||
memory.write(&temp, i * sizeof(T), sizeof(T));
|
||||
}
|
||||
|
||||
// Insert new element
|
||||
memory.write(&value, pos * sizeof(T), sizeof(T));
|
||||
++element_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap the contents of this vector with another
|
||||
* @param other Vector to swap with
|
||||
*/
|
||||
void swap(VectorHIMEM& other) noexcept {
|
||||
std::swap(memory, other.memory);
|
||||
std::swap(element_count, other.element_count);
|
||||
std::swap(element_capacity, other.element_capacity);
|
||||
}
|
||||
|
||||
protected:
|
||||
HimemBlock memory;
|
||||
size_t element_count = 0;
|
||||
size_t element_capacity = 0;
|
||||
static constexpr size_t min_elements = 16; // Minimum allocation size
|
||||
|
||||
/**
|
||||
* @brief Calculate required memory size in bytes for a given number of
|
||||
* elements
|
||||
* @param count Number of elements
|
||||
* @return Size in bytes
|
||||
*/
|
||||
static size_t calculate_size_bytes(size_t count) { return count * sizeof(T); }
|
||||
|
||||
/**
|
||||
* @brief Reallocate memory with a new capacity
|
||||
* @param new_capacity The new capacity to allocate
|
||||
* @return true if reallocation was successful, false otherwise
|
||||
*/
|
||||
bool reallocate(size_t new_capacity) {
|
||||
if (new_capacity <= element_capacity) {
|
||||
return true; // No need to reallocate
|
||||
}
|
||||
|
||||
// Calculate new size (at least min_elements)
|
||||
new_capacity = std::max(new_capacity, min_elements);
|
||||
|
||||
// Create a new memory block
|
||||
HimemBlock new_memory;
|
||||
element_capacity = new_memory.allocate(calculate_size_bytes(new_capacity)) / sizeof(T);
|
||||
if (element_capacity == 0) {
|
||||
ESP_LOGE(TAG, "Failed to allocate new memory block");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy existing elements if any
|
||||
if (element_count > 0) {
|
||||
T temp;
|
||||
for (size_t i = 0; i < element_count; ++i) {
|
||||
// Read from old memory
|
||||
memory.read(&temp, i * sizeof(T), sizeof(T));
|
||||
|
||||
// Write to new memory
|
||||
new_memory.write(&temp, i * sizeof(T), sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
// Swap the memory blocks
|
||||
memory = std::move(new_memory);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Equality comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if the vectors are equal, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator==(const VectorHIMEM<T>& lhs, const VectorHIMEM<T>& rhs) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T lhs_val, rhs_val;
|
||||
for (size_t i = 0; i < lhs.size(); ++i) {
|
||||
lhs_val = lhs[i]; // Uses operator[] which calls read()
|
||||
rhs_val = rhs[i];
|
||||
if (!(lhs_val == rhs_val)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if the vectors are not equal, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator!=(const VectorHIMEM<T>& lhs, const VectorHIMEM<T>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap the contents of two vectors
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
*/
|
||||
template <typename T>
|
||||
void swap(VectorHIMEM<T>& lhs, VectorHIMEM<T>& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace esp32_psram
|
||||
521
lib/esp32-psram/src/esp32-psram/VectorPSRAM.h
Normal file
521
lib/esp32-psram/src/esp32-psram/VectorPSRAM.h
Normal file
@@ -0,0 +1,521 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include "AllocatorPSRAM.h"
|
||||
|
||||
/**
|
||||
* @namespace esp32_psram
|
||||
* @brief Namespace containing ESP32 PSRAM-specific implementations
|
||||
*/
|
||||
namespace esp32_psram {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Equality comparison operator for allocators
|
||||
* @tparam T Type of first allocator
|
||||
* @tparam U Type of second allocator
|
||||
* @return Always true since all instances are equivalent
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
bool operator==(const AllocatorPSRAM<T>&, const AllocatorPSRAM<U>&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality comparison operator for allocators
|
||||
* @tparam T Type of first allocator
|
||||
* @tparam U Type of second allocator
|
||||
* @return Always false since all instances are equivalent
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const AllocatorPSRAM<T>&, const AllocatorPSRAM<U>&) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class VectorPSRAM
|
||||
* @brief Vector implementation that uses ESP32's PSRAM for storage
|
||||
* @tparam T Type of elements stored in the vector
|
||||
*
|
||||
* This class provides an interface identical to std::vector but allocates
|
||||
* all memory in ESP32's PSRAM, which helps preserve the limited internal RAM.
|
||||
* It wraps std::vector with a custom allocator that uses PSRAM.
|
||||
*/
|
||||
template <typename T>
|
||||
class VectorPSRAM {
|
||||
private:
|
||||
using vector_type = std::vector<T, AllocatorPSRAM<T>>;
|
||||
vector_type vec;
|
||||
|
||||
public:
|
||||
// Type definitions
|
||||
using value_type = typename vector_type::value_type;
|
||||
using allocator_type = typename vector_type::allocator_type;
|
||||
using size_type = typename vector_type::size_type;
|
||||
using difference_type = typename vector_type::difference_type;
|
||||
using reference = typename vector_type::reference;
|
||||
using const_reference = typename vector_type::const_reference;
|
||||
using pointer = typename vector_type::pointer;
|
||||
using const_pointer = typename vector_type::const_pointer;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
using reverse_iterator = typename vector_type::reverse_iterator;
|
||||
using const_reverse_iterator = typename vector_type::const_reverse_iterator;
|
||||
|
||||
/**
|
||||
* @brief Default constructor - creates an empty vector
|
||||
*/
|
||||
VectorPSRAM() : vec(AllocatorPSRAM<T>()) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a vector with the given number of default-initialized elements
|
||||
* @param count The size of the vector
|
||||
*/
|
||||
explicit VectorPSRAM(size_type count) : vec(count, AllocatorPSRAM<T>()) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a vector with the given number of copies of a value
|
||||
* @param count The size of the vector
|
||||
* @param value The value to initialize elements with
|
||||
*/
|
||||
VectorPSRAM(size_type count, const T& value) : vec(count, value, AllocatorPSRAM<T>()) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a vector with the contents of the range [first, last)
|
||||
* @tparam InputIt Input iterator type
|
||||
* @param first Iterator to the first element in the range
|
||||
* @param last Iterator to one past the last element in the range
|
||||
*/
|
||||
template <typename InputIt>
|
||||
VectorPSRAM(InputIt first, InputIt last) : vec(first, last, AllocatorPSRAM<T>()) {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* @param other The vector to copy from
|
||||
*/
|
||||
VectorPSRAM(const VectorPSRAM& other) : vec(other.vec) {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
* @param other The vector to move from
|
||||
*/
|
||||
VectorPSRAM(VectorPSRAM&& other) noexcept : vec(std::move(other.vec)) {}
|
||||
|
||||
/**
|
||||
* @brief Initializer list constructor
|
||||
* @param init The initializer list to copy from
|
||||
*/
|
||||
VectorPSRAM(std::initializer_list<T> init) : vec(init, AllocatorPSRAM<T>()) {}
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator
|
||||
* @param other The vector to copy from
|
||||
* @return Reference to this vector
|
||||
*/
|
||||
VectorPSRAM& operator=(const VectorPSRAM& other) {
|
||||
vec = other.vec;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator
|
||||
* @param other The vector to move from
|
||||
* @return Reference to this vector
|
||||
*/
|
||||
VectorPSRAM& operator=(VectorPSRAM&& other) noexcept {
|
||||
vec = std::move(other.vec);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializer list assignment operator
|
||||
* @param ilist The initializer list to copy from
|
||||
* @return Reference to this vector
|
||||
*/
|
||||
VectorPSRAM& operator=(std::initializer_list<T> ilist) {
|
||||
vec = ilist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element with bounds checking
|
||||
* @param pos The position of the element
|
||||
* @return Reference to the element at position pos
|
||||
* @throws std::out_of_range if pos is not within the range of the vector
|
||||
*/
|
||||
reference at(size_type pos) { return vec.at(pos); }
|
||||
|
||||
/**
|
||||
* @brief Access element with bounds checking (const version)
|
||||
* @param pos The position of the element
|
||||
* @return Const reference to the element at position pos
|
||||
* @throws std::out_of_range if pos is not within the range of the vector
|
||||
*/
|
||||
const_reference at(size_type pos) const { return vec.at(pos); }
|
||||
|
||||
/**
|
||||
* @brief Access element without bounds checking
|
||||
* @param pos The position of the element
|
||||
* @return Reference to the element at position pos
|
||||
*/
|
||||
reference operator[](size_type pos) { return vec[pos]; }
|
||||
|
||||
/**
|
||||
* @brief Access element without bounds checking (const version)
|
||||
* @param pos The position of the element
|
||||
* @return Const reference to the element at position pos
|
||||
*/
|
||||
const_reference operator[](size_type pos) const { return vec[pos]; }
|
||||
|
||||
/**
|
||||
* @brief Access the first element
|
||||
* @return Reference to the first element
|
||||
*/
|
||||
reference front() { return vec.front(); }
|
||||
|
||||
/**
|
||||
* @brief Access the first element (const version)
|
||||
* @return Const reference to the first element
|
||||
*/
|
||||
const_reference front() const { return vec.front(); }
|
||||
|
||||
/**
|
||||
* @brief Access the last element
|
||||
* @return Reference to the last element
|
||||
*/
|
||||
reference back() { return vec.back(); }
|
||||
|
||||
/**
|
||||
* @brief Access the last element (const version)
|
||||
* @return Const reference to the last element
|
||||
*/
|
||||
const_reference back() const { return vec.back(); }
|
||||
|
||||
/**
|
||||
* @brief Get pointer to the underlying array
|
||||
* @return Pointer to the underlying array
|
||||
*/
|
||||
T* data() noexcept { return vec.data(); }
|
||||
|
||||
/**
|
||||
* @brief Get pointer to the underlying array (const version)
|
||||
* @return Const pointer to the underlying array
|
||||
*/
|
||||
const T* data() const noexcept { return vec.data(); }
|
||||
|
||||
/**
|
||||
* @brief Get iterator to the beginning
|
||||
* @return Iterator to the first element
|
||||
*/
|
||||
iterator begin() noexcept { return vec.begin(); }
|
||||
|
||||
/**
|
||||
* @brief Get const iterator to the beginning
|
||||
* @return Const iterator to the first element
|
||||
*/
|
||||
const_iterator begin() const noexcept { return vec.begin(); }
|
||||
|
||||
/**
|
||||
* @brief Get const iterator to the beginning
|
||||
* @return Const iterator to the first element
|
||||
*/
|
||||
const_iterator cbegin() const noexcept { return vec.cbegin(); }
|
||||
|
||||
/**
|
||||
* @brief Get iterator to the end
|
||||
* @return Iterator to one past the last element
|
||||
*/
|
||||
iterator end() noexcept { return vec.end(); }
|
||||
|
||||
/**
|
||||
* @brief Get const iterator to the end
|
||||
* @return Const iterator to one past the last element
|
||||
*/
|
||||
const_iterator end() const noexcept { return vec.end(); }
|
||||
|
||||
/**
|
||||
* @brief Get const iterator to the end
|
||||
* @return Const iterator to one past the last element
|
||||
*/
|
||||
const_iterator cend() const noexcept { return vec.cend(); }
|
||||
|
||||
/**
|
||||
* @brief Get reverse iterator to the beginning
|
||||
* @return Reverse iterator to the first element
|
||||
*/
|
||||
reverse_iterator rbegin() noexcept { return vec.rbegin(); }
|
||||
|
||||
/**
|
||||
* @brief Get const reverse iterator to the beginning
|
||||
* @return Const reverse iterator to the first element
|
||||
*/
|
||||
const_reverse_iterator rbegin() const noexcept { return vec.rbegin(); }
|
||||
|
||||
/**
|
||||
* @brief Get const reverse iterator to the beginning
|
||||
* @return Const reverse iterator to the first element
|
||||
*/
|
||||
const_reverse_iterator crbegin() const noexcept { return vec.crbegin(); }
|
||||
|
||||
/**
|
||||
* @brief Get reverse iterator to the end
|
||||
* @return Reverse iterator to one past the last element
|
||||
*/
|
||||
reverse_iterator rend() noexcept { return vec.rend(); }
|
||||
|
||||
/**
|
||||
* @brief Get const reverse iterator to the end
|
||||
* @return Const reverse iterator to one past the last element
|
||||
*/
|
||||
const_reverse_iterator rend() const noexcept { return vec.rend(); }
|
||||
|
||||
/**
|
||||
* @brief Get const reverse iterator to the end
|
||||
* @return Const reverse iterator to one past the last element
|
||||
*/
|
||||
const_reverse_iterator crend() const noexcept { return vec.crend(); }
|
||||
|
||||
/**
|
||||
* @brief Check if the vector is empty
|
||||
* @return true if the vector is empty, false otherwise
|
||||
*/
|
||||
bool empty() const noexcept { return vec.empty(); }
|
||||
|
||||
/**
|
||||
* @brief Get the number of elements
|
||||
* @return The number of elements in the vector
|
||||
*/
|
||||
size_type size() const noexcept { return vec.size(); }
|
||||
|
||||
/**
|
||||
* @brief Get the maximum possible number of elements
|
||||
* @return The maximum possible number of elements the vector can hold
|
||||
*/
|
||||
size_type max_size() const noexcept { return vec.max_size(); }
|
||||
|
||||
/**
|
||||
* @brief Reserve storage
|
||||
* @param new_cap The new capacity of the vector
|
||||
* @throws std::length_error if new_cap > max_size()
|
||||
*/
|
||||
void reserve(size_type new_cap) { vec.reserve(new_cap); }
|
||||
|
||||
/**
|
||||
* @brief Get the number of elements that can be held in current storage
|
||||
* @return The capacity of the vector
|
||||
*/
|
||||
size_type capacity() const noexcept { return vec.capacity(); }
|
||||
|
||||
/**
|
||||
* @brief Reduce memory usage by freeing unused memory
|
||||
*/
|
||||
void shrink_to_fit() { vec.shrink_to_fit(); }
|
||||
|
||||
/**
|
||||
* @brief Clear the contents
|
||||
*/
|
||||
void clear() noexcept { vec.clear(); }
|
||||
|
||||
/**
|
||||
* @brief Insert an element
|
||||
* @param pos Iterator to the position before which the element will be inserted
|
||||
* @param value The value to insert
|
||||
* @return Iterator to the inserted element
|
||||
*/
|
||||
iterator insert(const_iterator pos, const T& value) { return vec.insert(pos, value); }
|
||||
|
||||
/**
|
||||
* @brief Insert an element by moving it
|
||||
* @param pos Iterator to the position before which the element will be inserted
|
||||
* @param value The value to insert
|
||||
* @return Iterator to the inserted element
|
||||
*/
|
||||
iterator insert(const_iterator pos, T&& value) { return vec.insert(pos, std::move(value)); }
|
||||
|
||||
/**
|
||||
* @brief Insert multiple copies of an element
|
||||
* @param pos Iterator to the position before which the elements will be inserted
|
||||
* @param count Number of copies to insert
|
||||
* @param value The value to insert
|
||||
* @return Iterator to the first inserted element
|
||||
*/
|
||||
iterator insert(const_iterator pos, size_type count, const T& value) { return vec.insert(pos, count, value); }
|
||||
|
||||
/**
|
||||
* @brief Insert elements from a range
|
||||
* @tparam InputIt Input iterator type
|
||||
* @param pos Iterator to the position before which the elements will be inserted
|
||||
* @param first Iterator to the first element in the range
|
||||
* @param last Iterator to one past the last element in the range
|
||||
* @return Iterator to the first inserted element
|
||||
*/
|
||||
template <typename InputIt>
|
||||
iterator insert(const_iterator pos, InputIt first, InputIt last) { return vec.insert(pos, first, last); }
|
||||
|
||||
/**
|
||||
* @brief Insert elements from an initializer list
|
||||
* @param pos Iterator to the position before which the elements will be inserted
|
||||
* @param ilist The initializer list to insert from
|
||||
* @return Iterator to the first inserted element
|
||||
*/
|
||||
iterator insert(const_iterator pos, std::initializer_list<T> ilist) { return vec.insert(pos, ilist); }
|
||||
|
||||
/**
|
||||
* @brief Construct an element in-place
|
||||
* @tparam Args Types of arguments to forward to the constructor
|
||||
* @param pos Iterator to the position before which the element will be constructed
|
||||
* @param args Arguments to forward to the constructor
|
||||
* @return Iterator to the inserted element
|
||||
*/
|
||||
template <typename... Args>
|
||||
iterator emplace(const_iterator pos, Args&&... args) { return vec.emplace(pos, std::forward<Args>(args)...); }
|
||||
|
||||
/**
|
||||
* @brief Erase an element
|
||||
* @param pos Iterator to the element to erase
|
||||
* @return Iterator to the element after the erased element
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return vec.erase(pos); }
|
||||
|
||||
/**
|
||||
* @brief Erase a range of elements
|
||||
* @param first Iterator to the first element to erase
|
||||
* @param last Iterator to one past the last element to erase
|
||||
* @return Iterator to the element after the erased range
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return vec.erase(first, last); }
|
||||
|
||||
/**
|
||||
* @brief Add an element to the end
|
||||
* @param value The value to append
|
||||
*/
|
||||
void push_back(const T& value) { vec.push_back(value); }
|
||||
|
||||
/**
|
||||
* @brief Add an element to the end by moving it
|
||||
* @param value The value to append
|
||||
*/
|
||||
void push_back(T&& value) { vec.push_back(std::move(value)); }
|
||||
|
||||
/**
|
||||
* @brief Construct an element in-place at the end
|
||||
* @tparam Args Types of arguments to forward to the constructor
|
||||
* @param args Arguments to forward to the constructor
|
||||
* @return Reference to the inserted element
|
||||
*/
|
||||
template <typename... Args>
|
||||
reference emplace_back(Args&&... args) { return vec.emplace_back(std::forward<Args>(args)...); }
|
||||
|
||||
/**
|
||||
* @brief Remove the last element
|
||||
*/
|
||||
void pop_back() { vec.pop_back(); }
|
||||
|
||||
/**
|
||||
* @brief Change the number of elements stored
|
||||
* @param count The new size of the vector
|
||||
*/
|
||||
void resize(size_type count) { vec.resize(count); }
|
||||
|
||||
/**
|
||||
* @brief Change the number of elements stored
|
||||
* @param count The new size of the vector
|
||||
* @param value The value to initialize new elements with
|
||||
*/
|
||||
void resize(size_type count, const value_type& value) { vec.resize(count, value); }
|
||||
|
||||
/**
|
||||
* @brief Swap the contents
|
||||
* @param other Vector to swap with
|
||||
*/
|
||||
void swap(VectorPSRAM& other) noexcept { vec.swap(other.vec); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Equality comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if the vectors are equal, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator==(const VectorPSRAM<T>& lhs, const VectorPSRAM<T>& rhs) {
|
||||
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if the vectors are not equal, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator!=(const VectorPSRAM<T>& lhs, const VectorPSRAM<T>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less than comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if lhs is lexicographically less than rhs, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator<(const VectorPSRAM<T>& lhs, const VectorPSRAM<T>& rhs) {
|
||||
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less than or equal comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if lhs is lexicographically less than or equal to rhs, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator<=(const VectorPSRAM<T>& lhs, const VectorPSRAM<T>& rhs) {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Greater than comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if lhs is lexicographically greater than rhs, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator>(const VectorPSRAM<T>& lhs, const VectorPSRAM<T>& rhs) {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Greater than or equal comparison operator
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
* @return true if lhs is lexicographically greater than or equal to rhs, false otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator>=(const VectorPSRAM<T>& lhs, const VectorPSRAM<T>& rhs) {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap the contents of two vectors
|
||||
* @tparam T Type of elements in the vectors
|
||||
* @param lhs First vector
|
||||
* @param rhs Second vector
|
||||
*/
|
||||
template <typename T>
|
||||
void swap(VectorPSRAM<T>& lhs, VectorPSRAM<T>& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace esp32_psram
|
||||
|
||||
Reference in New Issue
Block a user