mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-10-16 03:00:17 +00:00
Configuration moves to make pyxis buildable against vanilla
attermann/microReticulum @ 0.3.0 instead of the fork:
- lib/libbz2/ — pulled from torlando-tech/microReticulum @ ca355e5
(the previous submodule pin). Used by LXMF compression. Will
eventually move into the standalone microLXMF library (Track B).
- src-shim/ — the 37 fork-only files NOT already vendored into
pyxis/lib/ as part of the original split. Includes:
- LXMF/* (11 files) — to extract as a standalone library (Track B)
- Cryptography/{BZ2,Ratchet}.{cpp,h} — used by LXMF + RNS 1.x compat
- Instrumentation/{BootProfiler,MemoryMonitor}.{cpp,h} — pyxis-specific
- Utilities/{Print,Stream}.{cpp,h} — pyxis logging supplements
- root: Buffer, BytesPool, ChannelData, Display+Graphics, FileStream,
FileSystem, MessageBase, ObjectPool, PSRAMAllocator, SegmentAccumulator
- platformio.ini lib_deps: drop the symlink:// to deps/microReticulum/lib/libbz2
(gone in upstream), add microStore as a github URL dep (vanilla
upstream needs it for Bytes.h's microStore/Codec.h include).
- platformio.ini build_flags: add -Isrc-shim plus its subdirectories
so the vendored ../X.h-style includes (rewritten to <X.h>) resolve
through pyxis's own paths first.
src-shim/* was bulk-rewritten so `#include "../X.h"` (relative to
the fork's src/) becomes `#include <X.h>` resolved via -I deps/microReticulum/src.
Pyxis still doesn't build at this point — the next layer of breakage
is API-level (not file-level): pyxis main.cpp calls fork-only
Identity/LXMRouter/Transport methods that vanilla upstream doesn't
have. See the followup commit's notes / branch description for the
full list.
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
|