mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-07 03:09:49 +00:00
relocate shim to lib/; pyxis lib API renames Compile-tier graft progress on top of40e561f. Key changes: - Re-vendor lib/microreticulum-shim/ (was src-shim/) from the *actual* ca355e5 commit content rather than the stale "feat/t-deck HEAD" /tmp clone the previous commit pulled from. Recovers process_sync() on LXMRouter and MEMORY_MONITOR_POLL macro that were missing. - Move src-shim/ → lib/microreticulum-shim/ + add library.json so PlatformIO discovers the .cpp files and links them. Was previously only on the include path; the .cpps weren't in the build. (This unblocks the 30+ undefined-reference linker errors for LXMF and Instrumentation symbols.) - Drop -Isrc-shim/Utilities (/Cryptography/Instrumentation) from build_flags — they were over-broad and put our Stream.h on the GLOBAL header path, breaking Arduino's Wire.cpp which has `class TwoWire: public Stream`. -Ilib/microreticulum-shim alone resolves subdir lookups via <Cryptography/X.h>, <Utilities/Y.h>. - UniversalFileSystem migrated to microStore::Adapters::SPIFFSFileSystem (activated by -DUSTORE_USE_SPIFFS). Vanilla upstream microReticulum @ 0.3.0 deleted RNS::FileSystem in favor of microStore. Pyxis's lib/universal_filesystem/ is now dead code on this build path. - pyxis lib API renames for the post-graft world: SDLogger.cpp: RNS::setLogCallback -> RNS::set_log_callback AnnounceListScreen: Transport::get_destination_table -> Transport::get_path_table Transport::DestinationEntry -> RNS::Persistence::DestinationEntry UIManager.cpp: _lxst_destination ctor explicit RNS::Type::NONE (vanilla Destination has no default ctor) Identity::mark_persistent calls disabled w/ restoration TODO ConversationListScreen: Interface::get_rssi/get_stats calls disabled (the methods are non-virtual on BLEInterface/SX1262Interface post de-virtualization ina0ff631) Compile is clean against the fixed-cryptography submodule pin; current failure layer is fork-only Type::Channel constants referenced by the vendored shim's Buffer/ChannelData files. That's the next session's problem — see pyxis_microReticulum_graft_spike_findings.md for the plan options (most likely: remove Channel/Buffer/ChannelData/Ratchet from the shim, since LXMF doesn't use Channel anyway per the 2026-05-04 investigation).
161 lines
4.2 KiB
C++
161 lines
4.2 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <new>
|
|
#include <utility>
|
|
|
|
// FreeRTOS spinlock support - only on ESP32
|
|
#if defined(ESP_PLATFORM) || defined(ARDUINO)
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/portmacro.h"
|
|
#define OBJECTPOOL_USE_SPINLOCK 1
|
|
#else
|
|
// Native build - use std::mutex instead
|
|
#include <mutex>
|
|
#define OBJECTPOOL_USE_SPINLOCK 0
|
|
#endif
|
|
|
|
namespace RNS {
|
|
|
|
/**
|
|
* Fixed-size object pool with O(1) allocate/deallocate.
|
|
* Thread-safe via spinlock (ESP32) or mutex (native).
|
|
* Falls back to nullptr on exhaustion.
|
|
*
|
|
* Template parameters:
|
|
* T - Object type to pool
|
|
* N - Pool capacity (number of slots)
|
|
*
|
|
* Usage:
|
|
* ObjectPool<MyClass, 16> pool;
|
|
* MyClass* obj = pool.allocate(); // nullptr if exhausted
|
|
* if (obj) {
|
|
* // use object...
|
|
* pool.deallocate(obj);
|
|
* }
|
|
*
|
|
* Design notes:
|
|
* - Freelist stored in-place using union with storage
|
|
* - O(1) allocate: pop from freelist head
|
|
* - O(1) deallocate: push to freelist head
|
|
* - Placement new for construction, explicit destructor for cleanup
|
|
* - Pool exhaustion returns nullptr (caller should fall back to heap)
|
|
*/
|
|
template <typename T, size_t N>
|
|
class ObjectPool {
|
|
public:
|
|
ObjectPool() : _first_free(0), _allocated_count(0) {
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portMUX_INITIALIZE(&_mux);
|
|
#endif
|
|
// Initialize freelist chain
|
|
for (size_t i = 0; i < N - 1; i++) {
|
|
_slots[i].next_free = i + 1;
|
|
}
|
|
_slots[N - 1].next_free = INVALID_SLOT;
|
|
}
|
|
|
|
/**
|
|
* Allocate object from pool.
|
|
* Returns nullptr if pool exhausted (caller should fall back to heap).
|
|
* Thread-safe.
|
|
*
|
|
* Supports variadic constructor arguments via perfect forwarding.
|
|
* Example: pool.allocate(arg1, arg2) calls T(arg1, arg2)
|
|
*/
|
|
template<typename... Args>
|
|
T* allocate(Args&&... args) {
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portENTER_CRITICAL(&_mux);
|
|
#else
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
#endif
|
|
if (_first_free == INVALID_SLOT) {
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portEXIT_CRITICAL(&_mux);
|
|
#endif
|
|
return nullptr; // Pool exhausted
|
|
}
|
|
size_t slot = _first_free;
|
|
_first_free = _slots[slot].next_free;
|
|
_allocated_count++;
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portEXIT_CRITICAL(&_mux);
|
|
#endif
|
|
|
|
// Placement new to construct object with forwarded arguments
|
|
return new (&_slots[slot].storage) T(std::forward<Args>(args)...);
|
|
}
|
|
|
|
/**
|
|
* Return object to pool.
|
|
* Pointer must have been obtained from this pool's allocate().
|
|
* Thread-safe.
|
|
*/
|
|
void deallocate(T* ptr) {
|
|
if (!ptr) return;
|
|
|
|
// Calculate slot index from pointer
|
|
uintptr_t offset = reinterpret_cast<uintptr_t>(ptr) -
|
|
reinterpret_cast<uintptr_t>(_slots);
|
|
size_t slot = offset / sizeof(Slot);
|
|
|
|
if (slot >= N) {
|
|
// Not from this pool - ignore (caller's responsibility)
|
|
return;
|
|
}
|
|
|
|
// Explicit destructor call
|
|
ptr->~T();
|
|
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portENTER_CRITICAL(&_mux);
|
|
#else
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
#endif
|
|
_slots[slot].next_free = _first_free;
|
|
_first_free = slot;
|
|
_allocated_count--;
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portEXIT_CRITICAL(&_mux);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Check if pointer was allocated from this pool.
|
|
*/
|
|
bool owns(T* ptr) const {
|
|
if (!ptr) return false;
|
|
uintptr_t start = reinterpret_cast<uintptr_t>(_slots);
|
|
uintptr_t end = start + sizeof(_slots);
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
|
|
return addr >= start && addr < end;
|
|
}
|
|
|
|
size_t allocated() const { return _allocated_count; }
|
|
size_t capacity() const { return N; }
|
|
size_t available() const { return N - _allocated_count; }
|
|
|
|
private:
|
|
static constexpr size_t INVALID_SLOT = ~size_t(0);
|
|
|
|
struct Slot {
|
|
union {
|
|
alignas(T) char storage[sizeof(T)];
|
|
size_t next_free;
|
|
};
|
|
};
|
|
|
|
Slot _slots[N];
|
|
size_t _first_free;
|
|
size_t _allocated_count;
|
|
#if OBJECTPOOL_USE_SPINLOCK
|
|
portMUX_TYPE _mux;
|
|
#else
|
|
std::mutex _mutex;
|
|
#endif
|
|
};
|
|
|
|
} // namespace RNS
|