Ken Van Hoeylandt 03a6285328
Add kernel memory functions & other memory-related changes (#590)
New Features

- Added policy-based memory allocation APIs with capability flags and optional alignment: `memory_alloc_with_policy`, `memory_realloc_with_policy`, `memory_calloc_with_policy`, and `memory_free`.
- Switched heap memory reporting to `memory_print_stats`.

Bug Fixes

- Improved allocation robustness with capability fallback behavior.
- Added overflow-safe handling for aligned zero-initialized allocations.
- Tightened const-correctness for generated device-tree device arrays.

Tests

- Added unit tests for default policy, alignment, zero-initialization, realloc preservation, freeing, and memory stats reporting.
2026-07-26 22:59:18 +02:00

116 lines
4.3 KiB
C

#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Capability flags that describe what a memory allocation needs or prefers. */
enum MemoryCapability {
/** Internal memory (non-external/non-PSRAM) memory. */
MEMORY_CAPABILITY_INTERNAL = 1u << 0,
/** External memory (e.g. PSRAM/SPIRAM). */
MEMORY_CAPABILITY_EXTERNAL = 1u << 1,
/** Usable for code execution. */
MEMORY_CAPABILITY_EXECUTABLE = 1u << 2,
/** Usable as a DMA source/destination. */
MEMORY_CAPABILITY_DMA = 1u << 3,
/** Usable for SIMD instructions. */
MEMORY_CAPABILITY_SIMD = 1u << 4,
};
/**
* @brief Describes the constraints an allocation must (or should) satisfy.
*
* `required` capabilities must all be satisfied or the allocation fails. `desired`
* capabilities are attempted alongside `required`, but implementations fall back to
* `required`-only if `required | desired` together can't be satisfied.
*/
struct MemoryPolicy {
/** A bitset of MemoryCapability flags that are required during allocation. */
uint16_t required;
/** A bitset of MemoryCapability flags that are preferable (but optional) during allocation. */
uint16_t desired;
/** Alignment (in bytes) of the returned pointer, or 0 for the platform default. Must be a power of 2. */
size_t alignment;
};
/** The default policy: no required/desired capabilities, no alignment requirement. */
extern const struct MemoryPolicy MEMORY_POLICY_DEFAULT;
/**
* @brief Logs current heap usage (internal and external, when applicable).
* No-op on platforms without heap capability tracking.
*/
void memory_print_stats();
/**
* @brief Allocates memory that satisfies the given policy.
* @param[in] size number of bytes to allocate
* @param[in] policy the allocation constraints
* @return the allocated memory, or NULL on failure
*/
void* memory_alloc_with_policy(size_t size, const struct MemoryPolicy* policy);
/**
* @brief Resizes a previous allocation, preserving its contents up to the smaller of the old and new size.
* @warning policy->alignment is not guaranteed to be preserved across a realloc - it is only
* honored on fresh allocations (memory_alloc_with_policy()/memory_calloc_with_policy()).
* @param[in] ptr memory previously returned by memory_alloc_with_policy(), memory_calloc_with_policy(),
* or memory_realloc_with_policy(), or NULL to allocate a new block
* @param[in] size new memory size in bytes
* @param[in] policy the policy for the new allocation
* @return the (possibly moved) allocated memory, or NULL on failure - in which case ptr is left untouched
*/
void* memory_realloc_with_policy(void* ptr, size_t size, const struct MemoryPolicy* policy);
/**
* @brief Allocates zero-initialized memory that satisfies the given policy.
* @param[in] count number of elements
* @param[in] size size of each element in bytes
* @param[in] policy the allocation constraints
* @return the allocated memory, or NULL on failure
*/
void* memory_calloc_with_policy(size_t count, size_t size, const struct MemoryPolicy* policy);
/**
* @brief Allocates memory using MEMORY_POLICY_DEFAULT.
* @param[in] size number of bytes to allocate
* @return the allocated memory, or NULL on failure
*/
inline void* memory_alloc(size_t size) {
return memory_alloc_with_policy(size, &MEMORY_POLICY_DEFAULT);
}
/**
* @brief Allocates zero-initialized memory using MEMORY_POLICY_DEFAULT.
* @param[in] count number of elements
* @param[in] size size of each element in bytes
* @return the allocated memory, or NULL on failure
*/
inline void* memory_calloc(size_t count, size_t size) {
return memory_calloc_with_policy(count, size, &MEMORY_POLICY_DEFAULT);
}
/**
* @brief Resizes a previous allocation using MEMORY_POLICY_DEFAULT. See memory_realloc_with_policy().
* @param[in] ptr memory previously returned by one of the memory_* allocation functions, or NULL
* @param[in] size new memory size in bytes
* @return the (possibly moved) allocated memory, or NULL on failure - in which case ptr is left untouched
*/
inline void* memory_realloc(void* ptr, size_t size) {
return memory_realloc_with_policy(ptr, size, &MEMORY_POLICY_DEFAULT);
}
/**
* @brief Frees memory previously returned by one of the memory_* allocation functions.
* @param[in] ptr the memory to free, or NULL (a no-op)
*/
void memory_free(void* ptr);
#ifdef __cplusplus
}
#endif