Initial commit: project docs and ignore rules

This commit is contained in:
Dr Jones
2026-05-03 23:20:16 -07:00
commit 8c372c3cb0
8 changed files with 1177 additions and 0 deletions

327
memory_manager.cpp Normal file
View File

@@ -0,0 +1,327 @@
#include "memory_manager.h"
#include <cstring>
#ifdef ARDUINO
#include <Arduino.h>
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#include <cstdio>
#define DEBUG_PRINT(x) printf("%s", (x))
#define DEBUG_PRINTLN(x) printf("%s\n", (x))
#endif
// Global memory pool instance
MemoryPool globalMemoryPool;
MemoryPool::MemoryPool() : next_free_offset(0), allocation_count(0), deallocation_count(0) {
// Initialize all blocks as unused
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
blocks[i].ptr = nullptr;
blocks[i].size = 0;
blocks[i].in_use = false;
blocks[i].magic = 0;
}
// Clear the memory pool
memset(pool_memory, 0, POOL_SIZE);
}
MemoryPool::~MemoryPool() {
// Check for memory leaks
uint32_t active_blocks = 0;
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].in_use) {
active_blocks++;
}
}
if (active_blocks > 0) {
#ifdef DEBUG_MEMORY
DEBUG_PRINT("WARNING: Memory pool destroyed with ");
DEBUG_PRINT(active_blocks);
DEBUG_PRINTLN(" active blocks!");
#endif
}
}
void* MemoryPool::allocate(size_t size) {
if (size == 0 || size > POOL_SIZE) {
return nullptr;
}
// Align size to 4-byte boundary for better performance
size = (size + 3) & ~3;
// Check if we have enough space
if (next_free_offset + size > POOL_SIZE) {
// Try to find a free block that was previously deallocated
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (!blocks[i].in_use && blocks[i].ptr != nullptr && blocks[i].size >= size) {
blocks[i].in_use = true;
blocks[i].magic = MAGIC_NUMBER;
allocation_count++;
return blocks[i].ptr;
}
}
return nullptr; // Out of memory
}
// Find a free block descriptor
size_t block_index = MAX_BLOCKS;
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (!blocks[i].in_use && blocks[i].ptr == nullptr) {
block_index = i;
break;
}
}
if (block_index == MAX_BLOCKS) {
return nullptr; // No free block descriptors
}
// Allocate from the pool
void* ptr = &pool_memory[next_free_offset];
// Set up the block descriptor
blocks[block_index].ptr = ptr;
blocks[block_index].size = size;
blocks[block_index].in_use = true;
blocks[block_index].magic = MAGIC_NUMBER;
next_free_offset += size;
allocation_count++;
return ptr;
}
bool MemoryPool::deallocate(void* ptr) {
if (ptr == nullptr) {
return false;
}
// Find the block
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].ptr == ptr && blocks[i].in_use) {
// Check magic number for corruption
if (blocks[i].magic != MAGIC_NUMBER) {
#ifdef DEBUG_MEMORY
DEBUG_PRINTLN("ERROR: Memory corruption detected during deallocation!");
#endif
return false;
}
blocks[i].in_use = false;
blocks[i].magic = 0;
deallocation_count++;
// Clear the memory for security
memset(ptr, 0, blocks[i].size);
return true;
}
}
return false; // Pointer not found
}
MemoryPool::MemoryStats MemoryPool::getStats() const {
MemoryStats stats;
stats.total_size = POOL_SIZE;
stats.allocations = allocation_count;
stats.deallocations = deallocation_count;
stats.active_blocks = 0;
stats.used_size = 0;
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].in_use) {
stats.active_blocks++;
stats.used_size += blocks[i].size;
}
}
stats.free_size = POOL_SIZE - stats.used_size;
return stats;
}
bool MemoryPool::checkIntegrity() const {
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].in_use && blocks[i].magic != MAGIC_NUMBER) {
return false;
}
}
return true;
}
void MemoryPool::reset() {
// Clear all blocks
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
blocks[i].ptr = nullptr;
blocks[i].size = 0;
blocks[i].in_use = false;
blocks[i].magic = 0;
}
next_free_offset = 0;
allocation_count = 0;
deallocation_count = 0;
// Clear the memory pool
memset(pool_memory, 0, POOL_SIZE);
}
void MemoryPool::printStats() const {
MemoryStats stats = getStats();
#ifdef ARDUINO
Serial.println("=== Memory Pool Statistics ===");
Serial.print("Total Size: ");
Serial.print(stats.total_size);
Serial.println(" bytes");
Serial.print("Used Size: ");
Serial.print(stats.used_size);
Serial.println(" bytes");
Serial.print("Free Size: ");
Serial.print(stats.free_size);
Serial.println(" bytes");
Serial.print("Active Blocks: ");
Serial.println(stats.active_blocks);
Serial.print("Total Allocations: ");
Serial.println(stats.allocations);
Serial.print("Total Deallocations: ");
Serial.println(stats.deallocations);
Serial.print("Memory Integrity: ");
Serial.println(checkIntegrity() ? "OK" : "CORRUPTED");
Serial.println("==============================");
#else
printf("=== Memory Pool Statistics ===\n");
printf("Total Size: %zu bytes\n", stats.total_size);
printf("Used Size: %zu bytes\n", stats.used_size);
printf("Free Size: %zu bytes\n", stats.free_size);
printf("Active Blocks: %u\n", stats.active_blocks);
printf("Total Allocations: %u\n", stats.allocations);
printf("Total Deallocations: %u\n", stats.deallocations);
printf("Memory Integrity: %s\n", checkIntegrity() ? "OK" : "CORRUPTED");
printf("==============================\n");
#endif
}
// SafeBuffer implementation
SafeBuffer::SafeBuffer(size_t capacity, MemoryPool* pool)
: data_(nullptr), size_(0), capacity_(capacity), pool_(pool), owns_memory_(false) {
if (capacity > 0) {
if (pool_) {
data_ = static_cast<uint8_t*>(pool_->allocate(capacity));
} else {
#ifdef ARDUINO
data_ = static_cast<uint8_t*>(malloc(capacity));
#else
data_ = new uint8_t[capacity];
#endif
}
if (data_) {
owns_memory_ = true;
memset(data_, 0, capacity);
}
}
}
SafeBuffer::~SafeBuffer() {
if (data_ && owns_memory_) {
if (pool_) {
pool_->deallocate(data_);
} else {
#ifdef ARDUINO
free(data_);
#else
delete[] data_;
#endif
}
}
}
SafeBuffer::SafeBuffer(SafeBuffer&& other) noexcept
: data_(other.data_), size_(other.size_), capacity_(other.capacity_),
pool_(other.pool_), owns_memory_(other.owns_memory_) {
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
other.owns_memory_ = false;
}
SafeBuffer& SafeBuffer::operator=(SafeBuffer&& other) noexcept {
if (this != &other) {
// Clean up current resources
if (data_ && owns_memory_) {
if (pool_) {
pool_->deallocate(data_);
} else {
#ifdef ARDUINO
free(data_);
#else
delete[] data_;
#endif
}
}
// Move from other
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
pool_ = other.pool_;
owns_memory_ = other.owns_memory_;
// Reset other
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
other.owns_memory_ = false;
}
return *this;
}
bool SafeBuffer::write(const void* data, size_t size, size_t offset) {
if (!data_ || !data || size == 0) {
return false;
}
if (offset + size > capacity_) {
return false; // Would exceed buffer capacity
}
memcpy(data_ + offset, data, size);
// Update size if we wrote beyond current size
if (offset + size > size_) {
size_ = offset + size;
}
return true;
}
bool SafeBuffer::read(void* data, size_t size, size_t offset) const {
if (!data_ || !data || size == 0) {
return false;
}
if (offset + size > size_) {
return false; // Would read beyond valid data
}
memcpy(data, data_ + offset, size);
return true;
}
bool SafeBuffer::append(const void* data, size_t size) {
return write(data, size, size_);
}
void SafeBuffer::clear() {
if (data_) {
memset(data_, 0, capacity_);
size_ = 0;
}
}